/******************************************************************************* * Copyright (c) 1998, 2015 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 v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * 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; public DeferredContentHandler(UnmarshalRecord parentRecord) { levelIndex = 0; events = new ArrayList<SAXEvent>(); 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); } } public void startPrefixMapping(String prefix, String uri) throws SAXException { StartPrefixMappingEvent event = new StartPrefixMappingEvent(prefix, uri); events.add(event); } public void endPrefixMapping(String prefix) throws SAXException { EndPrefixMappingEvent event = new EndPrefixMappingEvent(prefix); events.add(event); } 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 AttributeList 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; } 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); } } public void setDocumentLocator(Locator locator) { DocumentLocatorEvent event = new DocumentLocatorEvent(locator); events.add(event); } public void startDocument() throws SAXException { StartDocumentEvent event = new StartDocumentEvent(); events.add(event); } public void endDocument() throws SAXException { EndDocumentEvent event = new EndDocumentEvent(); events.add(event); } public void characters(char[] ch, int start, int length) throws SAXException { charactersOccurred = true; CharactersEvent event = new CharactersEvent(ch, start, length); events.add(event); } public void characters(CharSequence characters) { charactersOccurred = true; CharactersEvent event = new CharactersEvent(characters); events.add(event); } public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { IgnorableWhitespaceEvent event = new IgnorableWhitespaceEvent(ch, start, length); events.add(event); } public void processingInstruction(String target, String data) throws SAXException { ProcessingInstructionEvent event = new ProcessingInstructionEvent(target, data); events.add(event); } public void skippedEntity(String name) throws SAXException { SkippedEntityEvent event = new SkippedEntityEvent(name); events.add(event); } public void startDTD(String name, String publicId, String systemId) throws SAXException { StartDTDEvent event = new StartDTDEvent(name, publicId, systemId); events.add(event); } public void endDTD() throws SAXException { EndDTDEvent event = new EndDTDEvent(); events.add(event); } public void startEntity(String name) throws SAXException { StartEntityEvent event = new StartEntityEvent(name); events.add(event); } public void endEntity(String name) throws SAXException { EndEntityEvent event = new EndEntityEvent(name); events.add(event); } public void startCDATA() throws SAXException { StartCDATAEvent event = new StartCDATAEvent(); events.add(event); } public void endCDATA() throws SAXException { EndCDATAEvent event = new EndCDATAEvent(); events.add(event); } 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; } public String getQName(int index) { return qNames.get(index); } 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; } public String getType(int index) { return types[index]; } public String getType(String qname) { return types[getIndex(qname)]; } public int getIndex(String qname) { return qNames.indexOf(qname); } 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; } public int getLength() { return localNames.length; } public String getLocalName(int index) { return localNames[index]; } public String getURI(int index) { return uris[index]; } public String getValue(int index) { return values[index]; } public String getValue(String qname) { return values[getIndex(qname)]; } 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; } } }