1/* 2 * Copyright (c) 1997, 2014, 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 26package com.sun.tools.internal.xjc.model; 27 28import com.sun.codemodel.internal.JClass; 29import com.sun.codemodel.internal.JCodeModel; 30import com.sun.codemodel.internal.JPackage; 31import com.sun.tools.internal.xjc.ErrorReceiver; 32import com.sun.tools.internal.xjc.Options; 33import com.sun.tools.internal.xjc.Plugin; 34import com.sun.tools.internal.xjc.api.ClassNameAllocator; 35import com.sun.tools.internal.xjc.generator.bean.BeanGenerator; 36import com.sun.tools.internal.xjc.generator.bean.ImplStructureStrategy; 37import com.sun.tools.internal.xjc.model.nav.NClass; 38import com.sun.tools.internal.xjc.model.nav.NType; 39import com.sun.tools.internal.xjc.model.nav.NavigatorImpl; 40import com.sun.tools.internal.xjc.outline.Outline; 41import com.sun.tools.internal.xjc.reader.xmlschema.Messages; 42import com.sun.tools.internal.xjc.util.ErrorReceiverFilter; 43import com.sun.xml.internal.bind.api.impl.NameConverter; 44import com.sun.xml.internal.bind.v2.model.core.Ref; 45import com.sun.xml.internal.bind.v2.model.core.TypeInfoSet; 46import com.sun.xml.internal.bind.v2.model.nav.Navigator; 47import com.sun.xml.internal.bind.v2.util.FlattenIterator; 48import com.sun.xml.internal.xsom.XSComponent; 49import com.sun.xml.internal.xsom.XSSchemaSet; 50import org.xml.sax.Locator; 51import org.xml.sax.SAXException; 52import org.xml.sax.helpers.LocatorImpl; 53 54import javax.xml.bind.annotation.XmlAttribute; 55import javax.xml.bind.annotation.XmlNsForm; 56import javax.xml.bind.annotation.XmlTransient; 57import javax.xml.namespace.QName; 58import javax.xml.transform.Result; 59import java.util.Collections; 60import java.util.HashMap; 61import java.util.HashSet; 62import java.util.Iterator; 63import java.util.LinkedHashMap; 64import java.util.Map; 65import java.util.Set; 66 67/** 68 * Root of the object model that represents the code that needs to be generated. 69 * 70 * <p> 71 * A {@link Model} is a schema language neutral representation of the 72 * result of a schema parsing. The back-end then works against this model 73 * to turn this into a series of Java source code. 74 * 75 * @author Kohsuke Kawaguchi 76 */ 77public final class Model implements TypeInfoSet<NType,NClass,Void,Void>, CCustomizable { 78 79 /** 80 * Generated beans. 81 */ 82 private final Map<NClass,CClassInfo> beans = new LinkedHashMap<NClass,CClassInfo>(); 83 84 /** 85 * Generated enums. 86 */ 87 private final Map<NClass,CEnumLeafInfo> enums = new LinkedHashMap<NClass,CEnumLeafInfo>(); 88 89 /** 90 * The element mappings. 91 */ 92 private final Map<NClass/*scope*/,Map<QName,CElementInfo>> elementMappings = 93 new LinkedHashMap<NClass, Map<QName, CElementInfo>>(); 94 95 private final Iterable<? extends CElementInfo> allElements = 96 new Iterable<CElementInfo>() { 97 public Iterator<CElementInfo> iterator() { 98 return new FlattenIterator<CElementInfo>(elementMappings.values()); 99 } 100 }; 101 102 /** 103 * {@link TypeUse}s for all named types. 104 * <p> 105 * I really don't want to promote the notion of a 'type' in any place except in the XML Schema code, 106 * but this needs to be exposed for JAX-RPC. A reference to a named XML type will be converted into 107 * a reference to a Java type with annotations. 108 */ 109 private final Map<QName,TypeUse> typeUses = new LinkedHashMap<QName, TypeUse>(); 110 111 /** 112 * {@link NameConverter} to be used. 113 */ 114 private NameConverter nameConverter; 115 116 /** 117 * Single linked list that connects all {@link CCustomizations} that belong to this model. 118 * 119 * @see CCustomizations#next 120 */ 121 /*package*/ CCustomizations customizations; 122 123 /** 124 * This field controls the generation of package level annotations for s2j 125 */ 126 private boolean packageLevelAnnotations = true; 127 128 /** 129 * If this model was built from XML Schema, this field 130 * stores the root object of the parse schema model. 131 * Otherwise null. 132 * 133 * @since 2.1.1 134 */ 135 public final XSSchemaSet schemaComponent; 136 137 private CCustomizations globalCustomizations = new CCustomizations(); 138 139 /** 140 * @param nc 141 * Usually this should be set in the constructor, but we do allow this parameter 142 * to be initially null, and then set later. 143 * @param schemaComponent 144 * The source schema model, if this is built from XSD. 145 */ 146 public Model( Options opts, JCodeModel cm, NameConverter nc, ClassNameAllocator allocator, XSSchemaSet schemaComponent ) { 147 this.options = opts; 148 this.codeModel = cm; 149 this.nameConverter = nc; 150 this.defaultSymbolSpace = new SymbolSpace(codeModel); 151 defaultSymbolSpace.setType(codeModel.ref(Object.class)); 152 153 elementMappings.put(null, new LinkedHashMap<QName, CElementInfo>()); 154 155 if(opts.automaticNameConflictResolution) 156 allocator = new AutoClassNameAllocator(allocator); 157 this.allocator = new ClassNameAllocatorWrapper(allocator); 158 this.schemaComponent = schemaComponent; 159 this.globalCustomizations.setParent(this, this); 160 } 161 162 public void setNameConverter(NameConverter nameConverter) { 163 assert this.nameConverter==null; 164 assert nameConverter!=null; 165 this.nameConverter = nameConverter; 166 } 167 168 /** 169 * Gets the name converter that shall be used to parse XML names into Java names. 170 */ 171 public final NameConverter getNameConverter() { 172 return nameConverter; 173 } 174 175 public boolean isPackageLevelAnnotations() { 176 return packageLevelAnnotations; 177 } 178 179 public void setPackageLevelAnnotations(boolean packageLevelAnnotations) { 180 this.packageLevelAnnotations = packageLevelAnnotations; 181 } 182 183 /** 184 * This model uses this code model exclusively. 185 */ 186 @XmlTransient 187 public final JCodeModel codeModel; 188 189 /** 190 * Command-line options used for building this model. 191 */ 192 public final Options options; 193 194 /** 195 * True to generate serializable classes. 196 */ 197 @XmlAttribute 198 public boolean serializable; 199 200 /** 201 * serial version UID to be generated. 202 * 203 * null if not to generate serialVersionUID field. 204 */ 205 @XmlAttribute 206 public Long serialVersionUID; 207 208 /** 209 * If non-null, all the generated classes should eventually derive from this class. 210 */ 211 @XmlTransient 212 public JClass rootClass; 213 214 /** 215 * If non-null, all the generated interfaces should eventually derive from this interface. 216 */ 217 @XmlTransient 218 public JClass rootInterface; 219 220 /** 221 * Specifies the code generation strategy. 222 * Must not be null. 223 */ 224 public ImplStructureStrategy strategy = ImplStructureStrategy.BEAN_ONLY; 225 226 /** 227 * This allocator has the final say on deciding the class name. 228 * Must not be null. 229 * 230 * <p> 231 * Model classes are responsible for using the allocator. 232 * This allocator interaction should be transparent to the user/builder 233 * of the model. 234 */ 235 /*package*/ final ClassNameAllocatorWrapper allocator; 236 237 /** 238 * Default ID/IDREF symbol space. Any ID/IDREF without explicit 239 * reference to a symbol space is assumed to use this default 240 * symbol space. 241 */ 242 @XmlTransient 243 public final SymbolSpace defaultSymbolSpace; 244 245 /** All the defined {@link SymbolSpace}s keyed by their name. */ 246 private final Map<String,SymbolSpace> symbolSpaces = new HashMap<String,SymbolSpace>(); 247 248 public SymbolSpace getSymbolSpace( String name ) { 249 SymbolSpace ss = symbolSpaces.get(name); 250 if(ss==null) 251 symbolSpaces.put(name,ss=new SymbolSpace(codeModel)); 252 return ss; 253 } 254 255 /** 256 * Fully-generate the source code into the given model. 257 * 258 * @return 259 * null if there was any errors. Otherwise it returns a valid 260 * {@link Outline} object, which captures how the model objects 261 * are mapped to the generated source code. 262 * <p> 263 * Add-ons can use those information to further augment the generated 264 * source code. 265 */ 266 public Outline generateCode(Options opt,ErrorReceiver receiver) { 267 ErrorReceiverFilter ehf = new ErrorReceiverFilter(receiver); 268 269 // run extensions // moved to BGMBuilder._build() - issue with hyperjaxb3 270// for( Plugin ma : opt.activePlugins ) 271// ma.postProcessModel(this,ehf); 272 273 Outline o = BeanGenerator.generate(this, ehf); 274 275 try {// run extensions 276 for( Plugin ma : opt.activePlugins ) 277 ma.run(o,opt,ehf); 278 } catch (SAXException e) { 279 // fatal error. error should have been reported 280 return null; 281 } 282 283 // check for unused plug-in customizations. 284 // these can be only checked after the plug-ins run, so it's here. 285 // the JAXB bindings are checked by XMLSchema's builder. 286 Set<CCustomizations> check = new HashSet<CCustomizations>(); 287 for( CCustomizations c=customizations; c!=null; c=c.next ) { 288 if(!check.add(c)) { 289 throw new AssertionError(); // detect a loop 290 } 291 for (CPluginCustomization p : c) { 292 if(!p.isAcknowledged()) { 293 ehf.error( 294 p.locator, 295 Messages.format( 296 Messages.ERR_UNACKNOWLEDGED_CUSTOMIZATION, 297 p.element.getNodeName() 298 )); 299 ehf.error( 300 c.getOwner().getLocator(), 301 Messages.format( 302 Messages.ERR_UNACKNOWLEDGED_CUSTOMIZATION_LOCATION)); 303 } 304 } 305 } 306 307 if(ehf.hadError()) 308 o = null; 309 return o; 310 } 311 312 /** 313 * Represents the "top-level binding". 314 * 315 * <p> 316 * This is used to support the use of a schema inside WSDL. 317 * For XML Schema, the top-level binding is a map from 318 * global element declarations to its representation class. 319 * 320 * <p> 321 * For other schema languages, it should follow the appendices in 322 * WSDL (but in practice no one would use WSDL with a schema language 323 * other than XML Schema, so it doesn't really matter.) 324 * 325 * <p> 326 * This needs to be filled by the front-end. 327 */ 328 public final Map<QName,CClassInfo> createTopLevelBindings() { 329 Map<QName,CClassInfo> r = new HashMap<QName,CClassInfo>(); 330 for( CClassInfo b : beans().values() ) { 331 if(b.isElement()) 332 r.put(b.getElementName(),b); 333 } 334 return r; 335 } 336 337 public Navigator<NType,NClass,Void,Void> getNavigator() { 338 return NavigatorImpl.theInstance; 339 } 340 341 public CNonElement getTypeInfo(NType type) { 342 CBuiltinLeafInfo leaf = CBuiltinLeafInfo.LEAVES.get(type); 343 if(leaf!=null) return leaf; 344 345 return getClassInfo(getNavigator().asDecl(type)); 346 } 347 348 public CBuiltinLeafInfo getAnyTypeInfo() { 349 return CBuiltinLeafInfo.ANYTYPE; 350 } 351 352 public CNonElement getTypeInfo(Ref<NType,NClass> ref) { 353 // TODO: handle XmlValueList 354 assert !ref.valueList; 355 return getTypeInfo(ref.type); 356 } 357 358 public Map<NClass,CClassInfo> beans() { 359 return beans; 360 } 361 362 public Map<NClass,CEnumLeafInfo> enums() { 363 return enums; 364 } 365 366 public Map<QName,TypeUse> typeUses() { 367 return typeUses; 368 } 369 370 /** 371 * No array mapping generation for XJC. 372 */ 373 public Map<NType, ? extends CArrayInfo> arrays() { 374 return Collections.emptyMap(); 375 } 376 377 public Map<NType, ? extends CBuiltinLeafInfo> builtins() { 378 return CBuiltinLeafInfo.LEAVES; 379 } 380 381 public CClassInfo getClassInfo(NClass t) { 382 return beans.get(t); 383 } 384 385 public CElementInfo getElementInfo(NClass scope,QName name) { 386 Map<QName,CElementInfo> m = elementMappings.get(scope); 387 if(m!=null) { 388 CElementInfo r = m.get(name); 389 if(r!=null) return r; 390 } 391 return elementMappings.get(null).get(name); 392 } 393 394 public Map<QName,CElementInfo> getElementMappings(NClass scope) { 395 return elementMappings.get(scope); 396 } 397 398 public Iterable<? extends CElementInfo> getAllElements() { 399 return allElements; 400 } 401 402 /** 403 * @deprecated 404 * Always return null. Perhaps you are interested in {@link #schemaComponent}? 405 */ 406 public XSComponent getSchemaComponent() { 407 return null; 408 } 409 410 /** 411 * @deprecated 412 * No line number available for the "root" component. 413 */ 414 public Locator getLocator() { 415 LocatorImpl r = new LocatorImpl(); 416 r.setLineNumber(-1); 417 r.setColumnNumber(-1); 418 return r; 419 } 420 421 /** 422 * Gets the global customizations. 423 */ 424 public CCustomizations getCustomizations() { 425 return globalCustomizations; 426 } 427 428 /** 429 * Not implemented in the compile-time model. 430 */ 431 public Map<String, String> getXmlNs(String namespaceUri) { 432 return Collections.emptyMap(); 433 } 434 435 public Map<String, String> getSchemaLocations() { 436 return Collections.emptyMap(); 437 } 438 439 public XmlNsForm getElementFormDefault(String nsUri) { 440 throw new UnsupportedOperationException(); 441 } 442 443 public XmlNsForm getAttributeFormDefault(String nsUri) { 444 throw new UnsupportedOperationException(); 445 } 446 447 public void dump(Result out) { 448 // TODO 449 throw new UnsupportedOperationException(); 450 } 451 452 /*package*/ void add( CEnumLeafInfo e ) { 453 enums.put( e.getClazz(), e ); 454 } 455 456 /*package*/ void add( CClassInfo ci ) { 457 beans.put( ci.getClazz(), ci ); 458 } 459 460 /*package*/ void add( CElementInfo ei ) { 461 NClass clazz = null; 462 if(ei.getScope()!=null) 463 clazz = ei.getScope().getClazz(); 464 465 Map<QName,CElementInfo> m = elementMappings.get(clazz); 466 if(m==null) 467 elementMappings.put(clazz, m = new LinkedHashMap<QName, CElementInfo>()); 468 m.put(ei.getElementName(),ei); 469 } 470 471 472 private final Map<JPackage,CClassInfoParent.Package> cache = new HashMap<JPackage,CClassInfoParent.Package>(); 473 474 public CClassInfoParent.Package getPackage(JPackage pkg) { 475 CClassInfoParent.Package r = cache.get(pkg); 476 if(r==null) 477 cache.put(pkg,r=new CClassInfoParent.Package(pkg)); 478 return r; 479 } 480 481 /*package*/ static final Locator EMPTY_LOCATOR; 482 483 static { 484 LocatorImpl l = new LocatorImpl(); 485 l.setColumnNumber(-1); 486 l.setLineNumber(-1); 487 EMPTY_LOCATOR = l; 488 } 489} 490