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.dtd; 27 28 import java.util.ArrayList; 29 import java.util.HashSet; 30 import java.util.List; 31 import java.util.Set; 32 33 import javax.xml.namespace.QName; 34 35 import com.sun.tools.internal.xjc.model.CBuiltinLeafInfo; 36 import com.sun.tools.internal.xjc.model.CClassInfo; 37 import com.sun.tools.internal.xjc.model.CElementPropertyInfo; 38 import static com.sun.tools.internal.xjc.model.CElementPropertyInfo.CollectionMode.*; 39 import com.sun.tools.internal.xjc.model.CPropertyInfo; 40 import com.sun.tools.internal.xjc.model.CReferencePropertyInfo; 41 import com.sun.tools.internal.xjc.model.CTypeRef; 42 import com.sun.tools.internal.xjc.model.CValuePropertyInfo; 43 import com.sun.tools.internal.xjc.model.TypeUse; 44 import com.sun.tools.internal.xjc.reader.dtd.bindinfo.BIConversion; 45 import com.sun.tools.internal.xjc.reader.dtd.bindinfo.BIElement; 46 import com.sun.xml.internal.bind.v2.model.core.ID; 47 import com.sun.xml.internal.bind.v2.model.core.WildcardMode; 48 import com.sun.xml.internal.dtdparser.DTDEventListener; 49 50 import org.xml.sax.Locator; 51 52 /** 53 * DTD Element. 54 * 55 * <p> 56 * This class extends {@link Term} to participate in the content model tree. 57 * 58 * <p> 59 * This class is repsonsible for binding the element. 60 * 61 * @author Kohsuke Kawaguchi 62 */ 63 final class Element extends Term implements Comparable<Element> { 64 65 /** 66 * Name of the element. 67 */ 68 final String name; 69 70 private final TDTDReader owner; 71 72 /** 73 * @see DTDEventListener#endContentModel(String, short) 74 */ 75 private short contentModelType; 76 77 private Term contentModel; 78 79 /** 80 * True if this element is referenced from another element. 81 */ 82 boolean isReferenced; 83 84 /** 85 * If this element maps to a class, that class representation. 86 * Otherwise null. 87 */ 88 private CClassInfo classInfo; 89 90 /** 91 * True if {@link #classInfo} field is computed. 92 */ 93 private boolean classInfoComputed; 94 95 /** 96 * List of attribute properties on this element 97 */ 98 final List<CPropertyInfo> attributes = new ArrayList<CPropertyInfo>(); 99 100 /** 101 * Normalized blocks of the content model. 102 */ 103 private final List<Block> normalizedBlocks = new ArrayList<Block>(); 104 105 /** 106 * True if this element needs to be a class. 107 * 108 * Currently, if an element is referenced from a construct like (A|B|C), 109 * we require those A,B, and C to be a class. 110 */ 111 private boolean mustBeClass; 112 113 /** 114 * The source location where this element is defined. 115 */ 116 private Locator locator; 117 Element(TDTDReader owner,String name)118 public Element(TDTDReader owner,String name) { 119 this.owner = owner; 120 this.name = name; 121 } 122 normalize(List<Block> r, boolean optional)123 void normalize(List<Block> r, boolean optional) { 124 Block o = new Block(optional,false); 125 o.elements.add(this); 126 r.add(o); 127 } 128 addAllElements(Block b)129 void addAllElements(Block b) { 130 b.elements.add(this); 131 } 132 isOptional()133 boolean isOptional() { 134 return false; 135 } 136 isRepeated()137 boolean isRepeated() { 138 return false; 139 } 140 141 142 /** 143 * Define its content model. 144 */ define(short contentModelType, Term contentModel, Locator locator)145 void define(short contentModelType, Term contentModel, Locator locator) { 146 assert this.contentModel==null; // may not be called twice 147 this.contentModelType = contentModelType; 148 this.contentModel = contentModel; 149 this.locator = locator; 150 contentModel.normalize(normalizedBlocks,false); 151 152 for( Block b : normalizedBlocks ) { 153 if(b.isRepeated || b.elements.size()>1) { 154 for( Element e : b.elements ) { 155 owner.getOrCreateElement(e.name).mustBeClass = true; 156 } 157 } 158 } 159 } 160 161 /** 162 * When this element is an PCDATA-only content model, 163 * returns the conversion for it. Otherwise the behavior is undefined. 164 */ getConversion()165 private TypeUse getConversion() { 166 assert contentModel == Term.EMPTY; // this is PCDATA-only element 167 168 BIElement e = owner.bindInfo.element(name); 169 if(e!=null) { 170 BIConversion conv = e.getConversion(); 171 if(conv!=null) 172 return conv.getTransducer(); 173 } 174 return CBuiltinLeafInfo.STRING; 175 } 176 177 /** 178 * Return null if this class is not bound to a class. 179 */ getClassInfo()180 CClassInfo getClassInfo() { 181 if(!classInfoComputed) { 182 classInfoComputed = true; 183 classInfo = calcClass(); 184 } 185 return classInfo; 186 } 187 calcClass()188 private CClassInfo calcClass() { 189 BIElement e = owner.bindInfo.element(name); 190 if(e==null) { 191 if(contentModelType!=DTDEventListener.CONTENT_MODEL_MIXED 192 || !attributes.isEmpty() 193 || mustBeClass) 194 return createDefaultClass(); 195 if(contentModel!=Term.EMPTY) { 196 throw new UnsupportedOperationException("mixed content model not supported"); 197 } else { 198 // just #PCDATA 199 if(isReferenced) 200 return null; 201 else 202 // if no one else is referencing, assumed to be the root. 203 return createDefaultClass(); 204 } 205 } else { 206 return e.clazz; 207 } 208 } 209 createDefaultClass()210 private CClassInfo createDefaultClass() { 211 String className = owner.model.getNameConverter().toClassName(name); 212 QName tagName = new QName("",name); 213 214 return new CClassInfo(owner.model,owner.getTargetPackage(),className,locator,null,tagName,null,null/*TODO*/); 215 } 216 bind()217 void bind() { 218 CClassInfo ci = getClassInfo(); 219 assert ci!=null || attributes.isEmpty(); 220 for( CPropertyInfo p : attributes ) 221 ci.addProperty(p); 222 223 switch(contentModelType) { 224 case DTDEventListener.CONTENT_MODEL_ANY: 225 CReferencePropertyInfo rp = new CReferencePropertyInfo("Content",true,false,true,null,null/*TODO*/,locator, false, false, false); 226 rp.setWildcard(WildcardMode.SKIP); 227 ci.addProperty(rp); 228 return; 229 case DTDEventListener.CONTENT_MODEL_CHILDREN: 230 break; // handling follows 231 case DTDEventListener.CONTENT_MODEL_MIXED: 232 if(contentModel!=Term.EMPTY) 233 throw new UnsupportedOperationException("mixed content model unsupported yet"); 234 235 if(ci!=null) { 236 // if this element is mapped to a class, just put one property 237 CValuePropertyInfo p = new CValuePropertyInfo("value", null,null/*TODO*/,locator,getConversion(),null); 238 ci.addProperty(p); 239 } 240 return; 241 case DTDEventListener.CONTENT_MODEL_EMPTY: 242 // no content model 243 assert ci!=null; 244 return; 245 } 246 247 // normalize 248 List<Block> n = new ArrayList<Block>(); 249 contentModel.normalize(n,false); 250 251 {// check collision among Blocks 252 Set<String> names = new HashSet<String>(); 253 boolean collision = false; 254 255 OUTER: 256 for( Block b : n ) 257 for( Element e : b.elements ) 258 if(!names.add(e.name)) { 259 collision = true; 260 break OUTER; 261 } 262 263 if(collision) { 264 // collapse all blocks into one 265 Block all = new Block(true,true); 266 for( Block b : n ) 267 all.elements.addAll(b.elements); 268 n.clear(); 269 n.add(all); 270 } 271 } 272 273 for( Block b : n ) { 274 CElementPropertyInfo p; 275 if(b.isRepeated || b.elements.size()>1) { 276 // collection 277 StringBuilder name = new StringBuilder(); 278 for( Element e : b.elements ) { 279 if(name.length()>0) 280 name.append("Or"); 281 name.append(owner.model.getNameConverter().toPropertyName(e.name)); 282 } 283 p = new CElementPropertyInfo(name.toString(), REPEATED_ELEMENT, ID.NONE, null, null,null/*TODO*/, locator, !b.isOptional ); 284 for( Element e : b.elements ) { 285 CClassInfo child = owner.getOrCreateElement(e.name).getClassInfo(); 286 assert child!=null; // we are requiring them to be classes. 287 p.getTypes().add(new CTypeRef(child,new QName("",e.name),null,false,null)); 288 } 289 } else { 290 // single property 291 String name = b.elements.iterator().next().name; 292 String propName = owner.model.getNameConverter().toPropertyName(name); 293 294 TypeUse refType; 295 Element ref = owner.getOrCreateElement(name); 296 if(ref.getClassInfo()!=null) 297 refType = ref.getClassInfo(); 298 else { 299 refType = ref.getConversion().getInfo(); 300 } 301 302 p = new CElementPropertyInfo(propName, 303 refType.isCollection()?REPEATED_VALUE:NOT_REPEATED, ID.NONE, null, null,null/*TODO*/, locator, !b.isOptional ); 304 305 p.getTypes().add(new CTypeRef(refType.getInfo(),new QName("",name),null,false,null)); 306 } 307 ci.addProperty(p); 308 } 309 } 310 compareTo(Element that)311 public int compareTo(Element that) { 312 return this.name.compareTo(that.name); 313 } 314 } 315