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 26 package com.sun.tools.internal.xjc.generator.bean; 27 28 import java.util.Collections; 29 import java.util.HashMap; 30 import java.util.HashSet; 31 import java.util.Map; 32 import java.util.Set; 33 34 import javax.xml.bind.annotation.XmlNsForm; 35 import javax.xml.bind.annotation.XmlSchema; 36 import javax.xml.namespace.QName; 37 38 import com.sun.codemodel.internal.JDefinedClass; 39 import com.sun.codemodel.internal.JPackage; 40 import com.sun.tools.internal.xjc.generator.annotation.spec.XmlSchemaWriter; 41 import com.sun.tools.internal.xjc.model.CAttributePropertyInfo; 42 import com.sun.tools.internal.xjc.model.CClassInfo; 43 import com.sun.tools.internal.xjc.model.CElement; 44 import com.sun.tools.internal.xjc.model.CElementPropertyInfo; 45 import com.sun.tools.internal.xjc.model.CPropertyInfo; 46 import com.sun.tools.internal.xjc.model.CPropertyVisitor; 47 import com.sun.tools.internal.xjc.model.CReferencePropertyInfo; 48 import com.sun.tools.internal.xjc.model.CTypeRef; 49 import com.sun.tools.internal.xjc.model.CValuePropertyInfo; 50 import com.sun.tools.internal.xjc.model.Model; 51 import com.sun.tools.internal.xjc.outline.PackageOutline; 52 import com.sun.tools.internal.xjc.outline.Aspect; 53 54 /** 55 * {@link PackageOutline} enhanced with schema2java specific 56 * information. 57 * 58 * @author 59 * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com), Martin Grebac (martin.grebac@oracle.com) 60 */ 61 public final class PackageOutlineImpl implements PackageOutline { 62 private final Model _model; 63 private final JPackage _package; 64 private final ObjectFactoryGenerator objectFactoryGenerator; 65 66 /*package*/ final Set<ClassOutlineImpl> classes = new HashSet<ClassOutlineImpl>(); 67 private final Set<ClassOutlineImpl> classesView = Collections.unmodifiableSet(classes); 68 69 private String mostUsedNamespaceURI; 70 private XmlNsForm elementFormDefault; 71 private XmlNsForm attributeFormDefault; 72 73 /** 74 * The namespace URI most commonly used in classes in this package. 75 * This should be used as the namespace URI for {@link XmlSchema#namespace()}. 76 * 77 * <p> 78 * Null if no default 79 * 80 * @see #calcDefaultValues(). 81 */ 82 public String getMostUsedNamespaceURI() { 83 return mostUsedNamespaceURI; 84 } 85 86 /** 87 * The attribute form default for this package. 88 * <p> 89 * The value is computed by examining what would yield the smallest generated code. 90 */ 91 public XmlNsForm getAttributeFormDefault() { 92 assert attributeFormDefault!=null; 93 return attributeFormDefault; 94 } 95 96 /** 97 * The element form default for this package. 98 * <p> 99 * The value is computed by examining what would yield the smallest generated code. 100 */ 101 public XmlNsForm getElementFormDefault() { 102 assert elementFormDefault!=null; 103 return elementFormDefault; 104 } 105 106 public JPackage _package() { 107 return _package; 108 } 109 110 public ObjectFactoryGenerator objectFactoryGenerator() { 111 return objectFactoryGenerator; 112 } 113 114 public Set<ClassOutlineImpl> getClasses() { 115 return classesView; 116 } 117 118 public JDefinedClass objectFactory() { 119 return objectFactoryGenerator.getObjectFactory(); 120 } 121 122 protected PackageOutlineImpl( BeanGenerator outline, Model model, JPackage _pkg ) { 123 this._model = model; 124 this._package = _pkg; 125 switch(model.strategy) { 126 case BEAN_ONLY: 127 objectFactoryGenerator = new PublicObjectFactoryGenerator(outline,model,_pkg); 128 break; 129 case INTF_AND_IMPL: 130 objectFactoryGenerator = new DualObjectFactoryGenerator(outline,model,_pkg); 131 break; 132 default: 133 throw new IllegalStateException(); 134 } 135 } 136 137 /** 138 * Compute the most common namespace URI in this package 139 * (to put into {@link XmlSchema#namespace()} and what value 140 * we should put into {@link XmlSchema#elementFormDefault()}. 141 * 142 * This method is called after {@link #classes} field is filled up. 143 */ 144 public void calcDefaultValues() { 145 // short-circuit if xjc was told not to generate package level annotations in 146 // package-info.java 147 if(!_model.isPackageLevelAnnotations()) { 148 mostUsedNamespaceURI = ""; 149 elementFormDefault = XmlNsForm.UNQUALIFIED; 150 return; 151 } 152 153 // used to visit properties 154 CPropertyVisitor<Void> propVisitor = new CPropertyVisitor<Void>() { 155 public Void onElement(CElementPropertyInfo p) { 156 for (CTypeRef tr : p.getTypes()) { 157 countURI(propUriCountMap, tr.getTagName()); 158 } 159 return null; 160 } 161 162 public Void onReference(CReferencePropertyInfo p) { 163 for (CElement e : p.getElements()) { 164 countURI(propUriCountMap, e.getElementName()); 165 } 166 return null; 167 } 168 169 public Void onAttribute(CAttributePropertyInfo p) { 170 return null; 171 } 172 173 public Void onValue(CValuePropertyInfo p) { 174 return null; 175 } 176 }; 177 178 179 for (ClassOutlineImpl co : classes) { 180 CClassInfo ci = co.target; 181 countURI(uriCountMap, ci.getTypeName()); 182 countURI(uriCountMap, ci.getElementName()); 183 184 for( CPropertyInfo p : ci.getProperties() ) 185 p.accept(propVisitor); 186 } 187 mostUsedNamespaceURI = getMostUsedURI(uriCountMap); 188 189 elementFormDefault = getFormDefault(); 190 attributeFormDefault = XmlNsForm.UNQUALIFIED; 191 try { 192 XmlNsForm modelValue = _model.getAttributeFormDefault(mostUsedNamespaceURI); 193 attributeFormDefault = modelValue; 194 } catch (Exception e) { 195 // ignore and accept default 196 } 197 198 // generate package-info.java 199 // we won't get this far if the user specified -npa 200 if(!mostUsedNamespaceURI.equals("") || elementFormDefault==XmlNsForm.QUALIFIED || (attributeFormDefault == XmlNsForm.QUALIFIED)) { 201 XmlSchemaWriter w = _model.strategy.getPackage(_package, Aspect.IMPLEMENTATION).annotate2(XmlSchemaWriter.class); 202 if(!mostUsedNamespaceURI.equals("")) 203 w.namespace(mostUsedNamespaceURI); 204 if(elementFormDefault==XmlNsForm.QUALIFIED) 205 w.elementFormDefault(elementFormDefault); 206 if(attributeFormDefault==XmlNsForm.QUALIFIED) 207 w.attributeFormDefault(attributeFormDefault); 208 } 209 } 210 211 // Map to keep track of how often each type or element uri is used in this package 212 // mostly used to calculate mostUsedNamespaceURI 213 private HashMap<String, Integer> uriCountMap = new HashMap<String, Integer>(); 214 215 // Map to keep track of how often each property uri is used in this package 216 // used to calculate elementFormDefault 217 private HashMap<String, Integer> propUriCountMap = new HashMap<String, Integer>(); 218 219 /** 220 * pull the uri out of the specified QName and keep track of it in the 221 * specified hash map 222 * 223 * @param qname 224 */ 225 private void countURI(HashMap<String, Integer> map, QName qname) { 226 if (qname == null) return; 227 228 String uri = qname.getNamespaceURI(); 229 230 if (map.containsKey(uri)) { 231 map.put(uri, map.get(uri) + 1); 232 } else { 233 map.put(uri, 1); 234 } 235 } 236 237 /** 238 * Iterate through the hash map looking for the namespace used 239 * most frequently. Ties are arbitrarily broken by the order 240 * in which the map keys are iterated over. 241 * 242 * <p> 243 * Because JAX-WS often reassigns the "" namespace URI, 244 * and when that happens it unintentionally also renames (normally 245 * unqualified) local elements, prefer non-"" URI when there's a tie. 246 */ 247 private String getMostUsedURI(HashMap<String, Integer> map) { 248 String mostPopular = null; 249 int count = 0; 250 251 for (Map.Entry<String,Integer> e : map.entrySet()) { 252 String uri = e.getKey(); 253 int uriCount = e.getValue(); 254 if (mostPopular == null) { 255 mostPopular = uri; 256 count = uriCount; 257 } else { 258 if (uriCount > count || (uriCount==count && mostPopular.equals(""))) { 259 mostPopular = uri; 260 count = uriCount; 261 } 262 } 263 } 264 265 if (mostPopular == null) return ""; 266 return mostPopular; 267 } 268 269 /** 270 * Calculate the element form defaulting. 271 * 272 * Compare the most frequently used property URI to the most frequently used 273 * element/type URI. If they match, then return QUALIFIED 274 */ 275 private XmlNsForm getFormDefault() { 276 if (getMostUsedURI(propUriCountMap).equals("")) return XmlNsForm.UNQUALIFIED; 277 else return XmlNsForm.QUALIFIED; 278 } 279 280 }