1 /* 2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.internal.xjc.reader.internalizer; 27 28 import java.util.ArrayList; 29 import java.util.Collection; 30 import java.util.Iterator; 31 import java.util.List; 32 33 import javax.xml.bind.JAXBException; 34 import javax.xml.bind.Unmarshaller; 35 import javax.xml.bind.UnmarshallerHandler; 36 import javax.xml.validation.ValidatorHandler; 37 38 import com.sun.istack.internal.NotNull; 39 import com.sun.istack.internal.SAXParseException2; 40 import com.sun.tools.internal.xjc.ErrorReceiver; 41 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BIDeclaration; 42 import com.sun.tools.internal.xjc.reader.xmlschema.bindinfo.BindInfo; 43 import com.sun.tools.internal.xjc.util.ForkContentHandler; 44 import com.sun.tools.internal.xjc.util.DOMUtils; 45 import com.sun.xml.internal.xsom.SCD; 46 import com.sun.xml.internal.xsom.XSAnnotation; 47 import com.sun.xml.internal.xsom.XSComponent; 48 import com.sun.xml.internal.xsom.XSSchemaSet; 49 50 import org.w3c.dom.Element; 51 import org.xml.sax.SAXException; 52 import org.xml.sax.SAXParseException; 53 54 /** 55 * Set of binding nodes that have target nodes specified via SCD. 56 * 57 * This is parsed during {@link Internalizer} works on the tree, 58 * but applying this has to wait for {@link XSSchemaSet} to be parsed. 59 * 60 * @author Kohsuke Kawaguchi 61 * @see SCD 62 */ 63 public final class SCDBasedBindingSet { 64 65 /** 66 * Represents the target schema component of the 67 * customization identified by SCD. 68 * 69 * @author Kohsuke Kawaguchi 70 */ 71 final class Target { 72 /** 73 * SCDs can be specified via multiple steps, like: 74 * 75 * <xmp> 76 * <bindings scd="foo/bar"> 77 * <bindings scd="zot/xyz"> 78 * </xmp> 79 * 80 * This field and {@link #nextSibling} form a single-linked list that 81 * represent the children that shall be evaluated within this target. 82 * Think of it as {@code List<Target>}. 83 */ 84 private Target firstChild; 85 private final Target nextSibling; 86 87 /** 88 * Compiled SCD. 89 */ 90 private final @NotNull SCD scd; 91 92 /** 93 * The element on which SCD was found. 94 */ 95 private final @NotNull Element src; 96 97 /** 98 * Bindings that apply to this SCD. 99 */ 100 private final List<Element> bindings = new ArrayList<Element>(); 101 102 private Target(Target parent, Element src, SCD scd) { 103 if(parent==null) { 104 this.nextSibling = topLevel; 105 topLevel = this; 106 } else { 107 this.nextSibling = parent.firstChild; 108 parent.firstChild = this; 109 } 110 this.src = src; 111 this.scd = scd; 112 } 113 114 /** 115 * Adds a new binding declaration to be associated to the schema component 116 * identified by {@link #scd}. 117 */ 118 void addBinidng(Element binding) { 119 bindings.add(binding); 120 } 121 122 /** 123 * Applies bindings to the schema component for this and its siblings. 124 */ 125 private void applyAll(Collection<? extends XSComponent> contextNode) { 126 for( Target self=this; self!=null; self=self.nextSibling ) 127 self.apply(contextNode); 128 } 129 130 /** 131 * Applies bindings to the schema component for just this node. 132 */ 133 private void apply(Collection<? extends XSComponent> contextNode) { 134 // apply the SCD... 135 Collection<XSComponent> childNodes = scd.select(contextNode); 136 if(childNodes.isEmpty()) { 137 // no node matched 138 if(src.getAttributeNode("if-exists")!=null) { 139 // if this attribute exists, it's not an error if SCD didn't match. 140 return; 141 } 142 143 reportError( src, Messages.format(Messages.ERR_SCD_EVALUATED_EMPTY,scd) ); 144 return; 145 } 146 147 if(firstChild!=null) 148 firstChild.applyAll(childNodes); 149 150 if(!bindings.isEmpty()) { 151 // error to match more than one components 152 Iterator<XSComponent> itr = childNodes.iterator(); 153 XSComponent target = itr.next(); 154 if(itr.hasNext()) { 155 reportError( src, Messages.format(Messages.ERR_SCD_MATCHED_MULTIPLE_NODES,scd,childNodes.size()) ); 156 errorReceiver.error( target.getLocator(), Messages.format(Messages.ERR_SCD_MATCHED_MULTIPLE_NODES_FIRST) ); 157 errorReceiver.error( itr.next().getLocator(), Messages.format(Messages.ERR_SCD_MATCHED_MULTIPLE_NODES_SECOND) ); 158 } 159 160 // apply bindings to the target 161 for (Element binding : bindings) { 162 for (Element item : DOMUtils.getChildElements(binding)) { 163 String localName = item.getLocalName(); 164 165 if ("bindings".equals(localName)) 166 continue; // this should be already in Target.bindings of some SpecVersion. 167 168 try { 169 new DOMForestScanner(forest).scan(item,loader); 170 BIDeclaration decl = (BIDeclaration)unmarshaller.getResult(); 171 172 // add this binding to the target 173 XSAnnotation ann = target.getAnnotation(true); 174 BindInfo bi = (BindInfo)ann.getAnnotation(); 175 if(bi==null) { 176 bi = new BindInfo(); 177 ann.setAnnotation(bi); 178 } 179 bi.addDecl(decl); 180 } catch (SAXException e) { 181 // the error should have already been reported. 182 } catch (JAXBException e) { 183 // if validation didn't fail, then unmarshalling can't go wrong 184 throw new AssertionError(e); 185 } 186 } 187 } 188 } 189 } 190 } 191 192 private Target topLevel; 193 194 /** 195 * The forest where binding elements came from. Needed to report line numbers for errors. 196 */ 197 private final DOMForest forest; 198 199 200 // variables used only during the apply method 201 // 202 private ErrorReceiver errorReceiver; 203 private UnmarshallerHandler unmarshaller; 204 private ForkContentHandler loader; // unmarshaller+validator 205 206 SCDBasedBindingSet(DOMForest forest) { 207 this.forest = forest; 208 } 209 210 Target createNewTarget(Target parent, Element src, SCD scd) { 211 return new Target(parent,src,scd); 212 } 213 214 /** 215 * Applies the additional binding customizations. 216 */ 217 public void apply(XSSchemaSet schema, ErrorReceiver errorReceiver) { 218 if(topLevel!=null) { 219 this.errorReceiver = errorReceiver; 220 Unmarshaller u = BindInfo.getCustomizationUnmarshaller(); 221 this.unmarshaller = u.getUnmarshallerHandler(); 222 ValidatorHandler v = BindInfo.bindingFileSchema.newValidator(); 223 v.setErrorHandler(errorReceiver); 224 loader = new ForkContentHandler(v,unmarshaller); 225 226 topLevel.applyAll(schema.getSchemas()); 227 228 this.loader = null; 229 this.unmarshaller = null; 230 this.errorReceiver = null; 231 } 232 } 233 234 private void reportError( Element errorSource, String formattedMsg ) { 235 reportError( errorSource, formattedMsg, null ); 236 } 237 238 private void reportError( Element errorSource, 239 String formattedMsg, Exception nestedException ) { 240 241 SAXParseException e = new SAXParseException2( formattedMsg, 242 forest.locatorTable.getStartLocation(errorSource), 243 nestedException ); 244 errorReceiver.error(e); 245 } 246 }