com.sun.tools.xjc.reader.internalizer.Internalizer Maven / Gradle / Ivy
/*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the "License"). You may not use this file except
* in compliance with the License.
*
* You can obtain a copy of the license at
* https://jwsdp.dev.java.net/CDDLv1.0.html
* See the License for the specific language governing
* permissions and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* HEADER in each file and include the License file at
* https://jwsdp.dev.java.net/CDDLv1.0.html If applicable,
* add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your
* own identifying information: Portions Copyright [yyyy]
* [name of copyright owner]
*/
package com.sun.tools.xjc.reader.internalizer;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import com.sun.istack.SAXParseException2;
import com.sun.tools.xjc.ErrorReceiver;
import com.sun.tools.xjc.reader.Const;
import com.sun.tools.xjc.util.DOMUtils;
import com.sun.tools.xjc.util.EditDistance;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXParseException;
/**
* Internalizes external binding declarations.
*
* The static "transform" method is the entry point.
*
* @author
* Kohsuke Kawaguchi ([email protected])
*/
class Internalizer {
private static final XPathFactory xpf = XPathFactory.newInstance();
private final XPath xpath = xpf.newXPath();
/**
* Internalize all <jaxb:bindings> customizations in the given forest.
*/
static void transform( DOMForest forest ) {
new Internalizer( forest ).transform();
}
private Internalizer( DOMForest forest ) {
this.errorHandler = forest.getErrorHandler();
this.forest = forest;
}
/**
* DOMForest object currently being processed.
*/
private final DOMForest forest;
/**
* All errors found during the transformation is sent to this object.
*/
private ErrorReceiver errorHandler;
private void transform() {
Map targetNodes = new HashMap();
//
// identify target nodes for all
//
for (Element jaxbBindings : forest.outerMostBindings) {
// initially, the inherited context is itself
buildTargetNodeMap(jaxbBindings, jaxbBindings, targetNodes);
}
//
// then move them to their respective positions.
//
for (Element jaxbBindings : forest.outerMostBindings) {
move(jaxbBindings, targetNodes);
}
}
/**
* Validates attributes of a <jaxb:bindings> element.
*/
private void validate( Element bindings ) {
NamedNodeMap atts = bindings.getAttributes();
for( int i=0; i result ) {
// start by the inherited target
Node target = inheritedTarget;
validate(bindings); // validate this node
// look for @schemaLocation
if( bindings.getAttributeNode("schemaLocation")!=null ) {
String schemaLocation = bindings.getAttribute("schemaLocation");
try {
// absolutize this URI.
// TODO: use the URI class
// TODO: honor xml:base
schemaLocation = new URL(
new URL( forest.getSystemId(bindings.getOwnerDocument()) ),
schemaLocation ).toExternalForm();
} catch( MalformedURLException e ) {
; // continue with the original schemaLocation value
}
target = forest.get(schemaLocation);
if(target==null) {
reportError( bindings,
Messages.format(Messages.ERR_INCORRECT_SCHEMA_REFERENCE,
schemaLocation,
EditDistance.findNearest(schemaLocation,forest.listSystemIDs())));
return; // abort processing this
}
}
// look for @node
if( bindings.getAttributeNode("node")!=null ) {
String nodeXPath = bindings.getAttribute("node");
// evaluate this XPath
NodeList nlst;
try {
xpath.setNamespaceContext(new NamespaceContextImpl(bindings));
nlst = (NodeList)xpath.evaluate(nodeXPath,target,XPathConstants.NODESET);
} catch (XPathExpressionException e) {
reportError( bindings,
Messages.format(Messages.ERR_XPATH_EVAL,e.getMessage()),
e );
return; // abort processing this
}
if( nlst.getLength()==0 ) {
reportError( bindings,
Messages.format(Messages.NO_XPATH_EVAL_TO_NO_TARGET,
nodeXPath) );
return; // abort
}
if( nlst.getLength()!=1 ) {
reportError( bindings,
Messages.format(Messages.NO_XPATH_EVAL_TOO_MANY_TARGETS,
nodeXPath,nlst.getLength()) );
return; // abort
}
Node rnode = nlst.item(0);
if(!(rnode instanceof Element )) {
reportError( bindings,
Messages.format(Messages.NO_XPATH_EVAL_TO_NON_ELEMENT,
nodeXPath) );
return; // abort
}
if( !forest.logic.checkIfValidTargetNode(forest,bindings,(Element)rnode) ) {
reportError( bindings,
Messages.format(Messages.XPATH_EVAL_TO_NON_SCHEMA_ELEMENT,
nodeXPath,
rnode.getNodeName() ) );
return; // abort
}
target = rnode;
}
// update the result map
result.put( bindings, target );
// look for child and process them recursively
Element[] children = DOMUtils.getChildElements( bindings, Const.JAXB_NSURI, "bindings" );
for (Element value : children)
buildTargetNodeMap(value, target, result);
}
/**
* Moves JAXB customizations under their respective target nodes.
*/
private void move( Element bindings, Map targetNodes ) {
Node target = targetNodes.get(bindings);
if(target==null)
// this must be the result of an error on the external binding.
// recover from the error by ignoring this node
return;
Element[] children = DOMUtils.getChildElements(bindings);
for (Element item : children) {
if ("bindings".equals(item.getLocalName()))
// process child recursively
move(item, targetNodes);
else {
if (!(target instanceof Element)) {
if(target instanceof Document) {
// we set the context node to the document when @schemaLocation is used.
reportError(item,
Messages.format(Messages.NO_CONTEXT_NODE_SPECIFIED));
} else {
reportError(item,
Messages.format(Messages.CONTEXT_NODE_IS_NOT_ELEMENT));
}
return; // abort
}
if (!forest.logic.checkIfValidTargetNode(forest, item, (Element)target)) {
reportError(item,
Messages.format(Messages.ORPHANED_CUSTOMIZATION, item.getNodeName()));
return; // abort
}
// move this node under the target
moveUnder(item,(Element)target);
}
}
}
/**
* Moves the "decl" node under the "target" node.
*
* @param decl
* A JAXB customization element (e.g., <jaxb:class>)
*
* @param target
* XML Schema element under which the declaration should move.
* For example, <xs:element>
*/
private void moveUnder( Element decl, Element target ) {
Element realTarget = forest.logic.refineTarget(target);
declExtensionNamespace( decl, target );
// copy in-scope namespace declarations of the decl node
// to the decl node itself so that this move won't change
// the in-scope namespace bindings.
Element p = decl;
Set inscopes = new HashSet();
while(true) {
NamedNodeMap atts = p.getAttributes();
for( int i=0; i
* Note that this method doesn't use the default namespace
* even if it can.
*/
private String allocatePrefix( Element e, String nsUri ) {
// look for existing namespaces.
NamedNodeMap atts = e.getAttributes();
for( int i=0; i