1 /*
   2  * Copyright (c) 1997, 2012, 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.addon.episode;
  27 
  28 import java.io.File;
  29 import java.io.FileOutputStream;
  30 import java.io.IOException;
  31 import java.io.OutputStream;
  32 import java.util.ArrayList;
  33 import java.util.HashMap;
  34 import java.util.HashSet;
  35 import java.util.List;
  36 import java.util.Map;
  37 import java.util.Set;
  38 
  39 import com.sun.tools.internal.xjc.BadCommandLineException;
  40 import com.sun.tools.internal.xjc.Options;
  41 import com.sun.tools.internal.xjc.Plugin;
  42 import com.sun.tools.internal.xjc.outline.ClassOutline;
  43 import com.sun.tools.internal.xjc.outline.Outline;
  44 import com.sun.tools.internal.xjc.outline.EnumOutline;
  45 import com.sun.tools.internal.xjc.reader.Const;
  46 import com.sun.xml.internal.txw2.TXW;
  47 import com.sun.xml.internal.txw2.output.StreamSerializer;
  48 import com.sun.xml.internal.xsom.XSAnnotation;
  49 import com.sun.xml.internal.xsom.XSAttGroupDecl;
  50 import com.sun.xml.internal.xsom.XSAttributeDecl;
  51 import com.sun.xml.internal.xsom.XSAttributeUse;
  52 import com.sun.xml.internal.xsom.XSComplexType;
  53 import com.sun.xml.internal.xsom.XSComponent;
  54 import com.sun.xml.internal.xsom.XSContentType;
  55 import com.sun.xml.internal.xsom.XSDeclaration;
  56 import com.sun.xml.internal.xsom.XSElementDecl;
  57 import com.sun.xml.internal.xsom.XSFacet;
  58 import com.sun.xml.internal.xsom.XSIdentityConstraint;
  59 import com.sun.xml.internal.xsom.XSModelGroup;
  60 import com.sun.xml.internal.xsom.XSModelGroupDecl;
  61 import com.sun.xml.internal.xsom.XSNotation;
  62 import com.sun.xml.internal.xsom.XSParticle;
  63 import com.sun.xml.internal.xsom.XSSchema;
  64 import com.sun.xml.internal.xsom.XSSimpleType;
  65 import com.sun.xml.internal.xsom.XSWildcard;
  66 import com.sun.xml.internal.xsom.XSXPath;
  67 import com.sun.xml.internal.xsom.visitor.XSFunction;
  68 import com.sun.xml.internal.bind.v2.schemagen.episode.Bindings;
  69 import com.sun.xml.internal.bind.v2.schemagen.episode.SchemaBindings;
  70 
  71 import org.xml.sax.ErrorHandler;
  72 import org.xml.sax.SAXException;
  73 import org.xml.sax.SAXParseException;
  74 
  75 /**
  76  * Creates the episode file,
  77  *
  78  * @author Kohsuke Kawaguchi
  79  * @author Ben Tomasini (ben.tomasini@gmail.com)
  80  */
  81 public class PluginImpl extends Plugin {
  82 
  83     private File episodeFile;
  84 
  85     public String getOptionName() {
  86         return "episode";
  87     }
  88 
  89     public String getUsage() {
  90         return "  -episode <FILE>    :  generate the episode file for separate compilation";
  91     }
  92 
  93     public int parseArgument(Options opt, String[] args, int i) throws BadCommandLineException, IOException {
  94         if(args[i].equals("-episode")) {
  95             episodeFile = new File(opt.requireArgument("-episode",args,++i));
  96             return 2;
  97         }
  98         return 0;
  99     }
 100 
 101     /**
 102      * Capture all the generated classes from global schema components
 103      * and generate them in an episode file.
 104      */
 105     public boolean run(Outline model, Options opt, ErrorHandler errorHandler) throws SAXException {
 106         try {
 107             // reorganize qualifying components by their namespaces to
 108             // generate the list nicely
 109             Map<XSSchema, PerSchemaOutlineAdaptors> perSchema = new HashMap<XSSchema,PerSchemaOutlineAdaptors>();
 110             boolean hasComponentInNoNamespace = false;
 111 
 112             // Combine classes and enums into a single list
 113             List<OutlineAdaptor> outlines = new ArrayList<OutlineAdaptor>();
 114 
 115             for (ClassOutline co : model.getClasses()) {
 116                 XSComponent sc = co.target.getSchemaComponent();
 117                 String fullName = co.implClass.fullName();
 118                 String packageName = co.implClass.getPackage().name();
 119                 OutlineAdaptor adaptor = new OutlineAdaptor(sc,
 120                         OutlineAdaptor.OutlineType.CLASS, fullName, packageName);
 121                 outlines.add(adaptor);
 122             }
 123 
 124             for (EnumOutline eo : model.getEnums()) {
 125                 XSComponent sc = eo.target.getSchemaComponent();
 126                 String fullName = eo.clazz.fullName();
 127                 String packageName = eo.clazz.getPackage().name();
 128                 OutlineAdaptor adaptor = new OutlineAdaptor(sc,
 129                         OutlineAdaptor.OutlineType.ENUM, fullName, packageName);
 130                 outlines.add(adaptor);
 131             }
 132 
 133             for (OutlineAdaptor oa : outlines) {
 134                 XSComponent sc = oa.schemaComponent;
 135 
 136                 if (sc == null) continue;
 137                 if (!(sc instanceof XSDeclaration))
 138                     continue;
 139                 XSDeclaration decl = (XSDeclaration) sc;
 140                 if (decl.isLocal())
 141                     continue;   // local components cannot be referenced from outside, so no need to list.
 142 
 143                 PerSchemaOutlineAdaptors list = perSchema.get(decl.getOwnerSchema());
 144                 if (list == null) {
 145                     list = new PerSchemaOutlineAdaptors();
 146                     perSchema.put(decl.getOwnerSchema(), list);
 147                 }
 148 
 149                 list.add(oa);
 150 
 151                 if (decl.getTargetNamespace().equals(""))
 152                     hasComponentInNoNamespace = true;
 153             }
 154 
 155             OutputStream os = new FileOutputStream(episodeFile);
 156             Bindings bindings = TXW.create(Bindings.class, new StreamSerializer(os, "UTF-8"));
 157             if(hasComponentInNoNamespace) // otherwise jaxb binding NS should be the default namespace
 158                 bindings._namespace(Const.JAXB_NSURI,"jaxb");
 159             else
 160                 bindings._namespace(Const.JAXB_NSURI,"");
 161             bindings.version("2.1");
 162             bindings._comment("\n\n"+opt.getPrologComment()+"\n  ");
 163 
 164             // generate listing per schema
 165             for (Map.Entry<XSSchema,PerSchemaOutlineAdaptors> e : perSchema.entrySet()) {
 166                 PerSchemaOutlineAdaptors ps = e.getValue();
 167                 Bindings group = bindings.bindings();
 168                 String tns = e.getKey().getTargetNamespace();
 169                 if(!tns.equals(""))
 170                     group._namespace(tns,"tns");
 171 
 172                 group.scd("x-schema::"+(tns.equals("")?"":"tns"));
 173                 SchemaBindings schemaBindings = group.schemaBindings();
 174                                 schemaBindings.map(false);
 175                                 if (ps.packageNames.size() == 1)
 176                                 {
 177                                         final String packageName = ps.packageNames.iterator().next();
 178                                         if (packageName != null && packageName.length() > 0) {
 179                                                 schemaBindings._package().name(packageName);
 180                                         }
 181                                 }
 182 
 183                                 for (OutlineAdaptor oa : ps.outlineAdaptors) {
 184                     Bindings child = group.bindings();
 185                     oa.buildBindings(child);
 186                 }
 187                 group.commit(true);
 188             }
 189 
 190             bindings.commit();
 191 
 192             return true;
 193         } catch (IOException e) {
 194             errorHandler.error(new SAXParseException("Failed to write to "+episodeFile,null,e));
 195             return false;
 196         }
 197     }
 198 
 199     /**
 200      * Computes SCD.
 201      * This is fairly limited as JAXB can only map a certain kind of components to classes.
 202      */
 203     private static final XSFunction<String> SCD = new XSFunction<String>() {
 204         private String name(XSDeclaration decl) {
 205             if(decl.getTargetNamespace().equals(""))
 206                 return decl.getName();
 207             else
 208                 return "tns:"+decl.getName();
 209         }
 210 
 211         public String complexType(XSComplexType type) {
 212             return "~"+name(type);
 213         }
 214 
 215         public String simpleType(XSSimpleType simpleType) {
 216             return "~"+name(simpleType);
 217         }
 218 
 219         public String elementDecl(XSElementDecl decl) {
 220             return name(decl);
 221         }
 222 
 223         // the rest is doing nothing
 224         public String annotation(XSAnnotation ann) {
 225             throw new UnsupportedOperationException();
 226         }
 227 
 228         public String attGroupDecl(XSAttGroupDecl decl) {
 229             throw new UnsupportedOperationException();
 230         }
 231 
 232         public String attributeDecl(XSAttributeDecl decl) {
 233             throw new UnsupportedOperationException();
 234         }
 235 
 236         public String attributeUse(XSAttributeUse use) {
 237             throw new UnsupportedOperationException();
 238         }
 239 
 240         public String schema(XSSchema schema) {
 241             throw new UnsupportedOperationException();
 242         }
 243 
 244         public String facet(XSFacet facet) {
 245             throw new UnsupportedOperationException();
 246         }
 247 
 248         public String notation(XSNotation notation) {
 249             throw new UnsupportedOperationException();
 250         }
 251 
 252         public String identityConstraint(XSIdentityConstraint decl) {
 253             throw new UnsupportedOperationException();
 254         }
 255 
 256         public String xpath(XSXPath xpath) {
 257             throw new UnsupportedOperationException();
 258         }
 259 
 260         public String particle(XSParticle particle) {
 261             throw new UnsupportedOperationException();
 262         }
 263 
 264         public String empty(XSContentType empty) {
 265             throw new UnsupportedOperationException();
 266         }
 267 
 268         public String wildcard(XSWildcard wc) {
 269             throw new UnsupportedOperationException();
 270         }
 271 
 272         public String modelGroupDecl(XSModelGroupDecl decl) {
 273             throw new UnsupportedOperationException();
 274         }
 275 
 276         public String modelGroup(XSModelGroup group) {
 277             throw new UnsupportedOperationException();
 278         }
 279     };
 280 
 281     private final static class OutlineAdaptor {
 282 
 283         private enum OutlineType {
 284 
 285             CLASS(new BindingsBuilder() {
 286                 public void build(OutlineAdaptor adaptor, Bindings bindings) {
 287                     bindings.klass().ref(adaptor.implName);
 288 
 289                 }
 290             }),
 291             ENUM(new BindingsBuilder() {
 292                 public void build(OutlineAdaptor adaptor, Bindings bindings) {
 293                     bindings.typesafeEnumClass().ref(adaptor.implName);
 294                 }
 295             });
 296 
 297             private final BindingsBuilder bindingsBuilder;
 298 
 299             private OutlineType(BindingsBuilder bindingsBuilder) {
 300                 this.bindingsBuilder = bindingsBuilder;
 301             }
 302 
 303             private interface BindingsBuilder {
 304                 void build(OutlineAdaptor adaptor, Bindings bindings);
 305             }
 306 
 307         };
 308 
 309         private final XSComponent schemaComponent;
 310         private final OutlineType outlineType;
 311         private final String implName;
 312         private final String packageName;
 313 
 314         public OutlineAdaptor(XSComponent schemaComponent, OutlineType outlineType,
 315                               String implName, String packageName) {
 316             this.schemaComponent = schemaComponent;
 317             this.outlineType = outlineType;
 318             this.implName = implName;
 319             this.packageName = packageName;
 320         }
 321 
 322         private void buildBindings(Bindings bindings) {
 323             bindings.scd(schemaComponent.apply(SCD));
 324             outlineType.bindingsBuilder.build(this, bindings);
 325         }
 326     }
 327 
 328     private final static class PerSchemaOutlineAdaptors {
 329 
 330         private final List<OutlineAdaptor> outlineAdaptors = new ArrayList<OutlineAdaptor>();
 331 
 332         private final Set<String> packageNames = new HashSet<String>();
 333 
 334         private void add(OutlineAdaptor outlineAdaptor)
 335         {
 336                 this.outlineAdaptors.add(outlineAdaptor);
 337                 this.packageNames.add(outlineAdaptor.packageName);
 338         }
 339 
 340     }
 341 }