1 /*
   2  * Copyright (c) 1997, 2015, 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.dtd.bindinfo;
  27 
  28 import java.io.IOException;
  29 import java.util.Collection;
  30 import java.util.HashMap;
  31 import java.util.Map;
  32 
  33 import javax.xml.parsers.ParserConfigurationException;
  34 import javax.xml.parsers.SAXParserFactory;
  35 import javax.xml.validation.ValidatorHandler;
  36 
  37 import com.sun.codemodel.internal.ClassType;
  38 import com.sun.codemodel.internal.JClass;
  39 import com.sun.codemodel.internal.JClassAlreadyExistsException;
  40 import com.sun.codemodel.internal.JCodeModel;
  41 import com.sun.codemodel.internal.JDefinedClass;
  42 import com.sun.codemodel.internal.JPackage;
  43 import com.sun.istack.internal.SAXParseException2;
  44 import com.sun.tools.internal.xjc.AbortException;
  45 import com.sun.tools.internal.xjc.ErrorReceiver;
  46 import com.sun.tools.internal.xjc.SchemaCache;
  47 import com.sun.tools.internal.xjc.model.CCustomizations;
  48 import com.sun.tools.internal.xjc.model.CPluginCustomization;
  49 import com.sun.tools.internal.xjc.model.Model;
  50 import com.sun.tools.internal.xjc.reader.Const;
  51 import com.sun.tools.internal.xjc.util.CodeModelClassFactory;
  52 import com.sun.tools.internal.xjc.util.ErrorReceiverFilter;
  53 import com.sun.tools.internal.xjc.util.ForkContentHandler;
  54 
  55 import com.sun.xml.internal.bind.v2.util.XmlFactory;
  56 import javax.xml.parsers.DocumentBuilderFactory;
  57 import org.w3c.dom.Document;
  58 import org.w3c.dom.Element;
  59 import org.xml.sax.InputSource;
  60 import org.xml.sax.SAXException;
  61 import org.xml.sax.XMLReader;
  62 
  63 /**
  64  * Root of the binding information.
  65  */
  66 public class BindInfo
  67 {
  68     /** Controller object that can be used to report errors. */
  69     protected final ErrorReceiver errorReceiver;
  70 
  71     /*package*/ final Model model;
  72 
  73     /**
  74      * The -p option that should control the default Java package that
  75      * will contain the generated code. Null if unspecified. This takes
  76      * precedence over the value specified in the binding file.
  77      */
  78     private final String defaultPackage;
  79 
  80     public BindInfo(Model model, InputSource source, ErrorReceiver _errorReceiver) throws AbortException {
  81         this( model, parse(model,source,_errorReceiver), _errorReceiver);
  82     }
  83 
  84     public BindInfo(Model model, Document _dom, ErrorReceiver _errorReceiver) {
  85         this.model = model;
  86         this.dom = _dom.getDocumentElement();
  87         this.codeModel = model.codeModel;
  88         this.errorReceiver = _errorReceiver;
  89         this.classFactory = new CodeModelClassFactory(_errorReceiver);
  90         // TODO: decide name converter from the binding file
  91 
  92         this.defaultPackage = model.options.defaultPackage;
  93 
  94         // copy global customizations to the model
  95         model.getCustomizations().addAll(getGlobalCustomizations());
  96 
  97         // process element declarations
  98         for( Element ele : DOMUtil.getChildElements(dom,"element")) {
  99             BIElement e = new BIElement(this,ele);
 100             elements.put(e.name(),e);
 101         }
 102 
 103         // add built-in conversions
 104         BIUserConversion.addBuiltinConversions(this,conversions);
 105 
 106         // process conversion declarations
 107         for( Element cnv : DOMUtil.getChildElements(dom,"conversion")) {
 108             BIConversion c = new BIUserConversion(this,cnv);
 109             conversions.put(c.name(),c);
 110         }
 111         for( Element en : DOMUtil.getChildElements(dom,"enumeration")) {
 112             BIConversion c = BIEnumeration.create( en, this );
 113             conversions.put(c.name(),c);
 114         }
 115         // TODO: check the uniquness of conversion name
 116 
 117 
 118         // process interface definitions
 119         for( Element itf : DOMUtil.getChildElements(dom,"interface")) {
 120             BIInterface c = new BIInterface(itf);
 121             interfaces.put(c.name(),c);
 122         }
 123     }
 124 
 125 
 126     /** CodeModel object that is used by this binding file. */
 127     final JCodeModel codeModel;
 128 
 129     /** Wrap the codeModel object and automate error reporting. */
 130     final CodeModelClassFactory classFactory;
 131 
 132     /** DOM tree that represents binding info. */
 133     private final Element dom;
 134 
 135     /** Conversion declarations. */
 136     private final Map<String,BIConversion> conversions = new HashMap<String,BIConversion>();
 137 
 138     /** Element declarations keyed by names. */
 139     private final Map<String,BIElement> elements = new HashMap<String,BIElement>();
 140 
 141     /** interface declarations keyed by names. */
 142     private final Map<String,BIInterface> interfaces = new HashMap<String,BIInterface>();
 143 
 144 
 145     /** XJC extension namespace. */
 146     private static final String XJC_NS = Const.XJC_EXTENSION_URI;
 147 
 148 //
 149 //
 150 //    Exposed public methods
 151 //
 152 //
 153     /** Gets the serialVersionUID if it's turned on. */
 154     public Long getSerialVersionUID() {
 155         Element serial = DOMUtil.getElement(dom,XJC_NS,"serializable");
 156         if(serial==null)    return null;
 157 
 158         String v = DOMUtil.getAttribute(serial,"uid");
 159         if(v==null) v="1";
 160         return new Long(v);
 161     }
 162 
 163     /** Gets the xjc:superClass customization if it's turned on. */
 164     public JClass getSuperClass() {
 165         Element sc = DOMUtil.getElement(dom,XJC_NS,"superClass");
 166         if (sc == null) return null;
 167 
 168         JDefinedClass c;
 169 
 170         try {
 171             String v = DOMUtil.getAttribute(sc,"name");
 172             if(v==null)     return null;
 173             c = codeModel._class(v);
 174             c.hide();
 175         } catch (JClassAlreadyExistsException e) {
 176             c = e.getExistingClass();
 177         }
 178 
 179         return c;
 180     }
 181 
 182     /** Gets the xjc:superInterface customization if it's turned on. */
 183     public JClass getSuperInterface() {
 184         Element sc = DOMUtil.getElement(dom,XJC_NS,"superInterface");
 185         if (sc == null) return null;
 186 
 187         String name = DOMUtil.getAttribute(sc,"name");
 188         if (name == null) return null;
 189 
 190         JDefinedClass c;
 191 
 192         try {
 193             c = codeModel._class(name, ClassType.INTERFACE);
 194             c.hide();
 195         } catch (JClassAlreadyExistsException e) {
 196             c = e.getExistingClass();
 197         }
 198 
 199         return c;
 200     }
 201 
 202     /**
 203      * Gets the specified package name (options/@package).
 204      */
 205     public JPackage getTargetPackage() {
 206         if(model.options.defaultPackage!=null)
 207             // "-p" takes precedence over everything else
 208             return codeModel._package(model.options.defaultPackage);
 209 
 210         String p;
 211         if( defaultPackage!=null )
 212             p = defaultPackage;
 213         else
 214             p = getOption("package", "");
 215         return codeModel._package(p);
 216     }
 217 
 218     /**
 219      * Gets the conversion declaration from the binding info.
 220      *
 221      * @return
 222      *        A non-null valid BIConversion object.
 223      */
 224     public BIConversion conversion(String name) {
 225         BIConversion r = conversions.get(name);
 226         if (r == null)
 227             throw new AssertionError("undefined conversion name: this should be checked by the validator before we read it");
 228         return r;
 229     }
 230 
 231     /**
 232      * Gets the element declaration from the binding info.
 233      *
 234      * @return
 235      *        If there is no declaration with a given name,
 236      *        this method returns null.
 237      */
 238     public BIElement element( String name ) {
 239         return elements.get(name);
 240     }
 241     /** Iterates all {@link BIElement}s in a read-only set. */
 242     public Collection<BIElement> elements() {
 243         return elements.values();
 244     }
 245 
 246     /** Returns all {@link BIInterface}s in a read-only set. */
 247     public Collection<BIInterface> interfaces() {
 248         return interfaces.values();
 249     }
 250 
 251     /**
 252      * Gets the list of top-level {@link CPluginCustomization}s.
 253      */
 254     private CCustomizations getGlobalCustomizations() {
 255         CCustomizations r=null;
 256         for( Element e : DOMUtil.getChildElements(dom) ) {
 257             if(!model.options.pluginURIs.contains(e.getNamespaceURI()))
 258                 continue;   // this isn't a plugin customization
 259             if(r==null)
 260                 r = new CCustomizations();
 261             r.add(new CPluginCustomization(e, DOMLocator.getLocationInfo(e)));
 262         }
 263 
 264         if(r==null)     r = CCustomizations.EMPTY;
 265         return new CCustomizations(r);
 266     }
 267 
 268 
 269 
 270 
 271 //
 272 //
 273 //    Internal utility methods
 274 //
 275 //
 276 
 277 
 278     /** Gets the value from the option element. */
 279     private String getOption(String attName, String defaultValue) {
 280         Element opt = DOMUtil.getElement(dom,"options");
 281         if (opt != null) {
 282             String s = DOMUtil.getAttribute(opt,attName);
 283             if (s != null)
 284                 return s;
 285         }
 286         return defaultValue;
 287     }
 288 
 289     /**
 290      * Lazily parsed schema for the binding file.
 291      */
 292     private static SchemaCache bindingFileSchema = new SchemaCache("bindingfile.xsd", BindInfo.class);
 293 
 294     /**
 295      * Parses an InputSource into dom4j Document.
 296      * Returns null in case of an exception.
 297      */
 298     private static Document parse( Model model, InputSource is, ErrorReceiver receiver ) throws AbortException {
 299         try {
 300             ValidatorHandler validator = bindingFileSchema.newValidator();
 301 
 302             // set up the pipe line as :
 303             //              /-> extensionChecker -> validator
 304             //   parser-> -<
 305             //              \-> DOM builder
 306             SAXParserFactory pf = XmlFactory.createParserFactory(model.options.disableXmlSecurity);
 307             DocumentBuilderFactory domFactory = XmlFactory.createDocumentBuilderFactory(model.options.disableXmlSecurity);
 308             DOMBuilder builder = new DOMBuilder(domFactory);
 309 
 310             ErrorReceiverFilter controller = new ErrorReceiverFilter(receiver);
 311             validator.setErrorHandler(controller);
 312             XMLReader reader = pf.newSAXParser().getXMLReader();
 313             reader.setErrorHandler(controller);
 314 
 315             DTDExtensionBindingChecker checker = new DTDExtensionBindingChecker("", model.options, controller);
 316             checker.setContentHandler(validator);
 317 
 318             reader.setContentHandler(new ForkContentHandler(checker,builder));
 319 
 320             reader.parse(is);
 321 
 322             if(controller.hadError())   throw new AbortException();
 323             return (Document)builder.getDOM();
 324         } catch( IOException e ) {
 325             receiver.error( new SAXParseException2(e.getMessage(),null,e) );
 326         } catch( SAXException e ) {
 327             receiver.error( new SAXParseException2(e.getMessage(),null,e) );
 328         } catch( ParserConfigurationException e ) {
 329             receiver.error( new SAXParseException2(e.getMessage(),null,e) );
 330         }
 331 
 332         throw new AbortException();
 333     }
 334 }