| /* |
| * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| package org.eclipse.persistence.internal.oxm.record.deferred; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.persistence.internal.oxm.record.ExtendedContentHandler; |
| import org.eclipse.persistence.internal.oxm.record.UnmarshalRecord; |
| import org.eclipse.persistence.internal.oxm.record.XMLReader; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.Locator; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.ext.LexicalHandler; |
| |
| /** |
| * <p><b>Purpose</b>: ContentHandler to store events until we know if we are dealing with a simple, complex or empty element. |
| * <p><b>Responsibilities</b>:<ul> |
| * <li> Store events until will know if the element is simple, complex or empty |
| * <li> Return control to the original unmarshalRecord |
| * </ul> |
| */ |
| public abstract class DeferredContentHandler implements ExtendedContentHandler, LexicalHandler { |
| private int levelIndex; |
| private List<SAXEvent> events; |
| private UnmarshalRecord parent; |
| private boolean startOccurred; |
| private boolean charactersOccurred; |
| private boolean attributesOccurred; |
| |
| protected DeferredContentHandler(UnmarshalRecord parentRecord) { |
| levelIndex = 0; |
| events = new ArrayList<>(); |
| this.parent = parentRecord; |
| } |
| |
| |
| @Override |
| public void setNil(boolean isNil) {} |
| |
| protected abstract void processEmptyElement() throws SAXException; |
| |
| protected abstract void processComplexElement() throws SAXException; |
| |
| protected abstract void processSimpleElement() throws SAXException; |
| |
| protected void processEmptyElementWithAttributes() throws SAXException { |
| processEmptyElement(); |
| } |
| |
| protected void executeEvents(UnmarshalRecord unmarshalRecord) throws SAXException { |
| for (int i = 0; i < events.size(); i++) { |
| SAXEvent nextEvent = events.get(i); |
| nextEvent.processEvent(unmarshalRecord); |
| } |
| XMLReader parentXMLReader = parent.getXMLReader(); |
| if (parentXMLReader.getContentHandler().equals(this)) { |
| parentXMLReader.setContentHandler(unmarshalRecord); |
| parentXMLReader.setLexicalHandler(unmarshalRecord); |
| } |
| } |
| |
| @Override |
| public void startPrefixMapping(String prefix, String uri) throws SAXException { |
| StartPrefixMappingEvent event = new StartPrefixMappingEvent(prefix, uri); |
| events.add(event); |
| } |
| |
| @Override |
| public void endPrefixMapping(String prefix) throws SAXException { |
| EndPrefixMappingEvent event = new EndPrefixMappingEvent(prefix); |
| events.add(event); |
| } |
| |
| @Override |
| public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { |
| levelIndex++; |
| |
| //Copy attributes because some parsers reuse the Attributes object across start element events |
| Attributes copiedAttrs = buildAttributeList(atts); |
| |
| StartElementEvent event = new StartElementEvent(uri, localName, qName, copiedAttrs); |
| events.add(event); |
| |
| if (startOccurred) { |
| //we know it's complex and non-null |
| processComplexElement(); |
| return; |
| } |
| |
| startOccurred = true; |
| } |
| |
| protected Attributes buildAttributeList(Attributes attrs) throws SAXException { |
| int attrsLength = attrs.getLength(); |
| AttributeList attributes = new AttributeList(attrsLength); |
| for (int i = 0; i < attrsLength; i++) { |
| String qName = attrs.getQName(i); |
| String uri = attrs.getURI(i); |
| attributes.addAttribute(attrs.getLocalName(i), qName, uri, attrs.getType(i), attrs.getValue(i), i); |
| if(!javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI.equals(uri) && (null != qName && !qName.startsWith(javax.xml.XMLConstants.XMLNS_ATTRIBUTE))) { |
| attributesOccurred = true; |
| } |
| } |
| return attributes; |
| } |
| |
| @Override |
| public void endElement(String uri, String localName, String qName) throws SAXException { |
| levelIndex--; |
| EndElementEvent event = new EndElementEvent(uri, localName, qName); |
| events.add(event); |
| |
| if (charactersOccurred) { |
| //we know it is a simple element |
| processSimpleElement(); |
| } else if(startOccurred){ |
| //we know it is an empty element |
| if(attributesOccurred) { |
| processEmptyElementWithAttributes(); |
| } else { |
| processEmptyElement(); |
| } |
| } |
| |
| if ((levelIndex == 0) && (parent != null)) { |
| XMLReader xmlReader = parent.getXMLReader(); |
| xmlReader.setContentHandler(parent); |
| xmlReader.setLexicalHandler(parent); |
| } |
| } |
| |
| @Override |
| public void setDocumentLocator(Locator locator) { |
| DocumentLocatorEvent event = new DocumentLocatorEvent(locator); |
| events.add(event); |
| } |
| |
| @Override |
| public void startDocument() throws SAXException { |
| StartDocumentEvent event = new StartDocumentEvent(); |
| events.add(event); |
| } |
| |
| @Override |
| public void endDocument() throws SAXException { |
| EndDocumentEvent event = new EndDocumentEvent(); |
| events.add(event); |
| } |
| |
| @Override |
| public void characters(char[] ch, int start, int length) throws SAXException { |
| charactersOccurred = true; |
| CharactersEvent event = new CharactersEvent(ch, start, length); |
| events.add(event); |
| } |
| |
| @Override |
| public void characters(CharSequence characters) { |
| charactersOccurred = true; |
| CharactersEvent event = new CharactersEvent(characters); |
| events.add(event); |
| } |
| |
| @Override |
| public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { |
| IgnorableWhitespaceEvent event = new IgnorableWhitespaceEvent(ch, start, length); |
| events.add(event); |
| } |
| |
| @Override |
| public void processingInstruction(String target, String data) throws SAXException { |
| ProcessingInstructionEvent event = new ProcessingInstructionEvent(target, data); |
| events.add(event); |
| } |
| |
| @Override |
| public void skippedEntity(String name) throws SAXException { |
| SkippedEntityEvent event = new SkippedEntityEvent(name); |
| events.add(event); |
| } |
| |
| @Override |
| public void startDTD(String name, String publicId, String systemId) throws SAXException { |
| StartDTDEvent event = new StartDTDEvent(name, publicId, systemId); |
| events.add(event); |
| } |
| |
| @Override |
| public void endDTD() throws SAXException { |
| EndDTDEvent event = new EndDTDEvent(); |
| events.add(event); |
| } |
| |
| @Override |
| public void startEntity(String name) throws SAXException { |
| StartEntityEvent event = new StartEntityEvent(name); |
| events.add(event); |
| } |
| |
| @Override |
| public void endEntity(String name) throws SAXException { |
| EndEntityEvent event = new EndEntityEvent(name); |
| events.add(event); |
| } |
| |
| @Override |
| public void startCDATA() throws SAXException { |
| StartCDATAEvent event = new StartCDATAEvent(); |
| events.add(event); |
| } |
| |
| @Override |
| public void endCDATA() throws SAXException { |
| EndCDATAEvent event = new EndCDATAEvent(); |
| events.add(event); |
| } |
| |
| @Override |
| public void comment(char[] ch, int start, int length) throws SAXException { |
| CommentEvent event = new CommentEvent(ch, start, length); |
| events.add(event); |
| } |
| |
| protected UnmarshalRecord getParent() { |
| return parent; |
| } |
| |
| protected List getEvents() { |
| return events; |
| } |
| |
| // Made static final for performance reasons. |
| /** |
| * Implementation of Attributes - used to pass along a given node's attributes |
| * to the startElement method of the reader's content handler. |
| */ |
| private static final class AttributeList implements org.xml.sax.Attributes { |
| |
| private String[] localNames; |
| private String[] uris; |
| private String[] values; |
| private String[] types; |
| private ArrayList<String> qNames; |
| |
| public AttributeList(int size) { |
| qNames = new ArrayList(size); |
| localNames = new String[size]; |
| uris = new String[size]; |
| types= new String[size]; |
| values = new String[size]; |
| } |
| |
| public void addAttribute(String localName, String qName, String uri, String type, String value, int index) { |
| qNames.add(index, qName); |
| localNames[index] = localName; |
| uris[index] = uri; |
| types[index] = type; |
| values[index] = value; |
| } |
| |
| @Override |
| public String getQName(int index) { |
| return qNames.get(index); |
| } |
| |
| @Override |
| public String getType(String namespaceUri, String localName) { |
| |
| for(int i=0;i <localNames.length; i++){ |
| String nextLocalName = localNames[i]; |
| if(nextLocalName != null && localName!= null && localName.equals(nextLocalName)){ |
| String uriAtIndex = uris[i]; |
| if(uriAtIndex == null) { |
| uriAtIndex=""; |
| } |
| if(uriAtIndex.equals(namespaceUri)){ |
| return types[i]; |
| } |
| } |
| } |
| return null; |
| |
| } |
| |
| @Override |
| public String getType(int index) { |
| return types[index]; |
| } |
| |
| @Override |
| public String getType(String qname) { |
| return types[getIndex(qname)]; |
| } |
| |
| @Override |
| public int getIndex(String qname) { |
| return qNames.indexOf(qname); |
| } |
| |
| @Override |
| public int getIndex(String uri, String localName) { |
| |
| for(int i=0;i <localNames.length; i++){ |
| String nextLocalName = localNames[i]; |
| if(nextLocalName != null && localName!= null && localName.equals(nextLocalName)){ |
| String uriAtIndex = uris[i]; |
| if(uriAtIndex == null) { |
| uriAtIndex=""; |
| } |
| if(uriAtIndex.equals(uri)){ |
| return i; |
| } |
| } |
| } |
| return -1; |
| } |
| |
| @Override |
| public int getLength() { |
| return localNames.length; |
| } |
| |
| @Override |
| public String getLocalName(int index) { |
| return localNames[index]; |
| } |
| |
| @Override |
| public String getURI(int index) { |
| return uris[index]; |
| } |
| |
| @Override |
| public String getValue(int index) { |
| return values[index]; |
| } |
| |
| @Override |
| public String getValue(String qname) { |
| return values[getIndex(qname)]; |
| } |
| |
| @Override |
| public String getValue(String uri, String localName) { |
| for(int i=0;i <localNames.length; i++){ |
| String nextLocalName = localNames[i]; |
| if(nextLocalName != null && localName!= null && localName.equals(nextLocalName)){ |
| String uriAtIndex = uris[i]; |
| //handle null/empty namespace case |
| if(uriAtIndex == null) { |
| uriAtIndex=""; |
| } |
| if(uriAtIndex.equals(uri)){ |
| return values[i]; |
| } |
| } |
| } |
| return null; |
| } |
| |
| } |
| } |