View Javadoc
1   /*
2    * Copyright (c) 1997, 2011, 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.xmlschema.bindinfo;
27  
28  import java.io.FilterWriter;
29  import java.io.IOException;
30  import java.io.StringWriter;
31  import java.io.Writer;
32  import java.util.ArrayList;
33  import java.util.Iterator;
34  import java.util.List;
35  
36  import javax.xml.bind.JAXBContext;
37  import javax.xml.bind.JAXBException;
38  import javax.xml.bind.Unmarshaller;
39  import javax.xml.bind.annotation.XmlAnyElement;
40  import javax.xml.bind.annotation.XmlElement;
41  import javax.xml.bind.annotation.XmlMixed;
42  import javax.xml.bind.annotation.XmlRootElement;
43  import javax.xml.bind.annotation.XmlType;
44  import javax.xml.transform.Transformer;
45  import javax.xml.transform.TransformerException;
46  import javax.xml.transform.dom.DOMSource;
47  import javax.xml.transform.stream.StreamResult;
48  
49  import com.sun.codemodel.internal.JDocComment;
50  import com.sun.xml.internal.bind.v2.WellKnownNamespace;
51  import com.sun.tools.internal.xjc.SchemaCache;
52  import com.sun.tools.internal.xjc.model.CCustomizations;
53  import com.sun.tools.internal.xjc.model.CPluginCustomization;
54  import com.sun.tools.internal.xjc.model.Model;
55  import com.sun.tools.internal.xjc.reader.Ring;
56  import com.sun.tools.internal.xjc.reader.xmlschema.BGMBuilder;
57  import com.sun.xml.internal.bind.annotation.XmlLocation;
58  import com.sun.xml.internal.bind.marshaller.MinimumEscapeHandler;
59  import com.sun.xml.internal.xsom.XSComponent;
60  
61  import org.w3c.dom.Element;
62  import org.xml.sax.Locator;
63  
64  /**
65   * Container for customization declarations.
66   *
67   * We use JAXB ourselves and parse this object from "xs:annotation".
68   *
69   * @author
70   *     Kohsuke Kawaguchi (kohsuke,kawaguchi@sun.com)
71   */
72  @XmlRootElement(namespace= WellKnownNamespace.XML_SCHEMA,name="annotation")
73  @XmlType(namespace=WellKnownNamespace.XML_SCHEMA,name="foobar")
74  public final class BindInfo implements Iterable<BIDeclaration> {
75  
76      private BGMBuilder builder;
77  
78      @XmlLocation
79      private Locator location;
80  
81      /**
82       * Documentation taken from &lt;xs:documentation>s.
83       */
84      @XmlElement(namespace=WellKnownNamespace.XML_SCHEMA)
85      private Documentation documentation;
86  
87      /**
88       * Returns true if this {@link BindInfo} doesn't contain any useful
89       * information.
90       *
91       * This flag is used to discard unused {@link BindInfo}s early to save memory footprint.
92       */
93      public boolean isPointless() {
94          if(size()>0)     return false;
95          if(documentation!=null && !documentation.contents.isEmpty())
96              return false;
97  
98          return true;
99      }
100 
101     private static final class Documentation {
102         @XmlAnyElement
103         @XmlMixed
104         List<Object> contents = new ArrayList<Object>();
105 
106         void addAll(Documentation rhs) {
107             if(rhs==null)   return;
108 
109             if(contents==null)
110                 contents = new ArrayList<Object>();
111             if(!contents.isEmpty())
112                 contents.add("\n\n");
113             contents.addAll(rhs.contents);
114         }
115     }
116 
117     /** list of individual declarations. */
118     private final List<BIDeclaration> decls = new ArrayList<BIDeclaration>();
119 
120     private static final class AppInfo {
121         /**
122          * Receives {@link BIDeclaration}s and other DOMs.
123          */
124         @XmlAnyElement(lax=true,value=DomHandlerEx.class)
125         List<Object> contents = new ArrayList<Object>();
126 
127         public void addTo(BindInfo bi) {
128             if(contents==null)  return;
129 
130             for (Object o : contents) {
131                 if(o instanceof BIDeclaration)
132                     bi.addDecl((BIDeclaration)o);
133                 // this is really PITA! I can't get the source location
134                 if(o instanceof DomHandlerEx.DomAndLocation) {
135                     DomHandlerEx.DomAndLocation e = (DomHandlerEx.DomAndLocation)o;
136                     String nsUri = e.element.getNamespaceURI();
137                     if(nsUri==null || nsUri.equals("")
138                     || nsUri.equals(WellKnownNamespace.XML_SCHEMA))
139                         continue;   // this is definitely not a customization
140                     bi.addDecl(new BIXPluginCustomization(e.element,e.loc));
141                 }
142             }
143         }
144     }
145 
146 
147     // only used by JAXB
148     @XmlElement(namespace=WellKnownNamespace.XML_SCHEMA)
149     void setAppinfo(AppInfo aib) {
150         aib.addTo(this);
151     }
152 
153 
154 
155     /**
156      * Gets the location of this annotation in the source file.
157      *
158      * @return
159      *      If the declarations are in fact specified in the source
160      *      code, a non-null valid object will be returned.
161      *      If this BindInfo is generated internally by XJC, then
162      *      null will be returned.
163      */
164     public Locator getSourceLocation() { return location; }
165 
166 
167     private XSComponent owner;
168     /**
169      * Sets the owner schema component and a reference to BGMBuilder.
170      * This method is called from the BGMBuilder before
171      * any BIDeclaration inside it is used.
172      */
173     public void setOwner( BGMBuilder _builder, XSComponent _owner ) {
174         this.owner = _owner;
175         this.builder = _builder;
176         for (BIDeclaration d : decls)
177             d.onSetOwner();
178     }
179     public XSComponent getOwner() { return owner; }
180 
181     /**
182      * Back pointer to the BGMBuilder which is building
183      * a BGM from schema components including this customization.
184      */
185     public BGMBuilder getBuilder() { return builder; }
186 
187     /** Adds a new declaration. */
188     public void addDecl( BIDeclaration decl ) {
189         if(decl==null)  throw new IllegalArgumentException();
190         decl.setParent(this);
191         decls.add(decl);
192     }
193 
194     /**
195      * Gets the first declaration with a given name, or null
196      * if none is found.
197      */
198     public <T extends BIDeclaration>
199     T get( Class<T> kind ) {
200         for( BIDeclaration decl : decls ) {
201             if( kind.isInstance(decl) )
202                 return kind.cast(decl);
203         }
204         return null; // not found
205     }
206 
207     /**
208      * Gets all the declarations
209      */
210     public BIDeclaration[] getDecls() {
211         return decls.toArray(new BIDeclaration[decls.size()]);
212     }
213 
214     /**
215      * Gets the documentation parsed from &lt;xs:documentation>s.
216      * The returned collection is to be added to {@link JDocComment#append(Object)}.
217      * @return  maybe null.
218      */
219     public String getDocumentation() {
220         // TODO: FIXME: correctly turn individual items to String including DOM
221         if(documentation==null || documentation.contents==null) return null;
222 
223         StringBuilder buf = new StringBuilder();
224         for (Object c : documentation.contents) {
225             if(c instanceof String) {
226                 buf.append(c.toString());
227             }
228             if(c instanceof Element) {
229                 Transformer t = builder.getIdentityTransformer();
230                 StringWriter w = new StringWriter();
231                 try {
232                     Writer fw = new FilterWriter(w) {
233                         char[] buf = new char[1];
234 
235                         public void write(int c) throws IOException {
236                             buf[0] = (char)c;
237                             write(buf,0,1);
238                         }
239 
240                         public void write(char[] cbuf, int off, int len) throws IOException {
241                             MinimumEscapeHandler.theInstance.escape(cbuf,off,len,false,out);
242                         }
243 
244                         public void write(String str, int off, int len) throws IOException {
245                             write(str.toCharArray(),off,len);
246                         }
247                     };
248                     t.transform(new DOMSource((Element)c),new StreamResult(fw));
249                 } catch (TransformerException e) {
250                     throw new Error(e); // impossible
251                 }
252                 buf.append("\n<pre>\n");
253                 buf.append(w);
254                 buf.append("\n</pre>\n");
255             }
256         }
257         return buf.toString();
258     }
259 
260     /**
261      * Merges all the declarations inside the given BindInfo
262      * to this BindInfo.
263      */
264     public void absorb( BindInfo bi ) {
265         for( BIDeclaration d : bi )
266             d.setParent(this);
267         this.decls.addAll( bi.decls );
268 
269         if(this.documentation==null)
270             this.documentation = bi.documentation;
271         else
272             this.documentation.addAll(bi.documentation);
273     }
274 
275     /** Gets the number of declarations. */
276     public int size() { return decls.size(); }
277 
278     public BIDeclaration get( int idx ) { return decls.get(idx); }
279 
280     public Iterator<BIDeclaration> iterator() {
281         return decls.iterator();
282     }
283 
284     /**
285      * Gets the list of {@link CPluginCustomization}s from this.
286      *
287      * <p>
288      * Note that calling this method marks all those plug-in customizations
289      * as 'used'. So call it only when it's really necessary.
290      */
291     public CCustomizations toCustomizationList() {
292         CCustomizations r=null;
293         for( BIDeclaration d : this ) {
294             if(d instanceof BIXPluginCustomization) {
295                 BIXPluginCustomization pc = (BIXPluginCustomization) d;
296                 pc.markAsAcknowledged();
297                 if(!Ring.get(Model.class).options.pluginURIs.contains(pc.getName().getNamespaceURI()))
298                     continue;   // this isn't a plugin customization
299                 if(r==null)
300                     r = new CCustomizations();
301                 r.add(new CPluginCustomization(pc.element,pc.getLocation()));
302             }
303         }
304 
305         if(r==null)     r = CCustomizations.EMPTY;
306         return new CCustomizations(r);
307     }
308     /** An instance with the empty contents. */
309     public final static BindInfo empty = new BindInfo();
310 
311     /**
312      * Lazily prepared {@link JAXBContext}.
313      */
314     private static volatile JAXBContext customizationContext;
315 
316     public static JAXBContext getCustomizationContext() {
317         try {
318             if (customizationContext == null) {
319                 synchronized (BindInfo.class) {
320                     if (customizationContext == null) {
321                         customizationContext = JAXBContext.newInstance(
322                                 BindInfo.class, // for xs:annotation
323                                 BIClass.class,
324                                 BIConversion.User.class,
325                                 BIConversion.UserAdapter.class,
326                                 BIDom.class,
327                                 BIFactoryMethod.class,
328                                 BIInlineBinaryData.class,
329                                 BIXDom.class,
330                                 BIXSubstitutable.class,
331                                 BIEnum.class,
332                                 BIEnumMember.class,
333                                 BIGlobalBinding.class,
334                                 BIProperty.class,
335                                 BISchemaBinding.class);
336                     }
337                 }
338             }
339             return customizationContext;
340         } catch (JAXBException e) {
341             throw new AssertionError(e);
342         }
343     }
344 
345     public static Unmarshaller getCustomizationUnmarshaller() {
346         try {
347             return getCustomizationContext().createUnmarshaller();
348         } catch (JAXBException e) {
349             throw new AssertionError(e);
350         }
351     }
352 
353     /**
354      * Lazily parsed schema for the binding file.
355      */
356     public static final SchemaCache bindingFileSchema = new SchemaCache(BindInfo.class.getResource("binding.xsd"));
357 }