aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.tools.internal.xjc.generator.bean; aoqi@0: aoqi@0: import java.util.Collections; aoqi@0: import java.util.HashMap; aoqi@0: import java.util.HashSet; aoqi@0: import java.util.Map; aoqi@0: import java.util.Set; aoqi@0: aoqi@0: import javax.xml.bind.annotation.XmlNsForm; aoqi@0: import javax.xml.bind.annotation.XmlSchema; aoqi@0: import javax.xml.namespace.QName; aoqi@0: aoqi@0: import com.sun.codemodel.internal.JDefinedClass; aoqi@0: import com.sun.codemodel.internal.JPackage; aoqi@0: import com.sun.tools.internal.xjc.generator.annotation.spec.XmlSchemaWriter; aoqi@0: import com.sun.tools.internal.xjc.model.CAttributePropertyInfo; aoqi@0: import com.sun.tools.internal.xjc.model.CClassInfo; aoqi@0: import com.sun.tools.internal.xjc.model.CElement; aoqi@0: import com.sun.tools.internal.xjc.model.CElementPropertyInfo; aoqi@0: import com.sun.tools.internal.xjc.model.CPropertyInfo; aoqi@0: import com.sun.tools.internal.xjc.model.CPropertyVisitor; aoqi@0: import com.sun.tools.internal.xjc.model.CReferencePropertyInfo; aoqi@0: import com.sun.tools.internal.xjc.model.CTypeRef; aoqi@0: import com.sun.tools.internal.xjc.model.CValuePropertyInfo; aoqi@0: import com.sun.tools.internal.xjc.model.Model; aoqi@0: import com.sun.tools.internal.xjc.outline.PackageOutline; aoqi@0: import com.sun.tools.internal.xjc.outline.Aspect; aoqi@0: aoqi@0: /** aoqi@0: * {@link PackageOutline} enhanced with schema2java specific aoqi@0: * information. aoqi@0: * aoqi@0: * @author aoqi@0: * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com), Martin Grebac (martin.grebac@oracle.com) aoqi@0: */ aoqi@0: public final class PackageOutlineImpl implements PackageOutline { aoqi@0: private final Model _model; aoqi@0: private final JPackage _package; aoqi@0: private final ObjectFactoryGenerator objectFactoryGenerator; aoqi@0: aoqi@0: /*package*/ final Set classes = new HashSet(); aoqi@0: private final Set classesView = Collections.unmodifiableSet(classes); aoqi@0: aoqi@0: private String mostUsedNamespaceURI; aoqi@0: private XmlNsForm elementFormDefault; aoqi@0: private XmlNsForm attributeFormDefault; aoqi@0: aoqi@0: /** aoqi@0: * The namespace URI most commonly used in classes in this package. aoqi@0: * This should be used as the namespace URI for {@link XmlSchema#namespace()}. aoqi@0: * aoqi@0: *

aoqi@0: * Null if no default aoqi@0: * aoqi@0: * @see #calcDefaultValues(). aoqi@0: */ aoqi@0: public String getMostUsedNamespaceURI() { aoqi@0: return mostUsedNamespaceURI; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * The attribute form default for this package. aoqi@0: *

aoqi@0: * The value is computed by examining what would yield the smallest generated code. aoqi@0: */ aoqi@0: public XmlNsForm getAttributeFormDefault() { aoqi@0: assert attributeFormDefault!=null; aoqi@0: return attributeFormDefault; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * The element form default for this package. aoqi@0: *

aoqi@0: * The value is computed by examining what would yield the smallest generated code. aoqi@0: */ aoqi@0: public XmlNsForm getElementFormDefault() { aoqi@0: assert elementFormDefault!=null; aoqi@0: return elementFormDefault; aoqi@0: } aoqi@0: aoqi@0: public JPackage _package() { aoqi@0: return _package; aoqi@0: } aoqi@0: aoqi@0: public ObjectFactoryGenerator objectFactoryGenerator() { aoqi@0: return objectFactoryGenerator; aoqi@0: } aoqi@0: aoqi@0: public Set getClasses() { aoqi@0: return classesView; aoqi@0: } aoqi@0: aoqi@0: public JDefinedClass objectFactory() { aoqi@0: return objectFactoryGenerator.getObjectFactory(); aoqi@0: } aoqi@0: aoqi@0: protected PackageOutlineImpl( BeanGenerator outline, Model model, JPackage _pkg ) { aoqi@0: this._model = model; aoqi@0: this._package = _pkg; aoqi@0: switch(model.strategy) { aoqi@0: case BEAN_ONLY: aoqi@0: objectFactoryGenerator = new PublicObjectFactoryGenerator(outline,model,_pkg); aoqi@0: break; aoqi@0: case INTF_AND_IMPL: aoqi@0: objectFactoryGenerator = new DualObjectFactoryGenerator(outline,model,_pkg); aoqi@0: break; aoqi@0: default: aoqi@0: throw new IllegalStateException(); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Compute the most common namespace URI in this package aoqi@0: * (to put into {@link XmlSchema#namespace()} and what value aoqi@0: * we should put into {@link XmlSchema#elementFormDefault()}. aoqi@0: * aoqi@0: * This method is called after {@link #classes} field is filled up. aoqi@0: */ aoqi@0: public void calcDefaultValues() { aoqi@0: // short-circuit if xjc was told not to generate package level annotations in aoqi@0: // package-info.java aoqi@0: if(!_model.isPackageLevelAnnotations()) { aoqi@0: mostUsedNamespaceURI = ""; aoqi@0: elementFormDefault = XmlNsForm.UNQUALIFIED; aoqi@0: return; aoqi@0: } aoqi@0: aoqi@0: // used to visit properties aoqi@0: CPropertyVisitor propVisitor = new CPropertyVisitor() { aoqi@0: public Void onElement(CElementPropertyInfo p) { aoqi@0: for (CTypeRef tr : p.getTypes()) { aoqi@0: countURI(propUriCountMap, tr.getTagName()); aoqi@0: } aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: public Void onReference(CReferencePropertyInfo p) { aoqi@0: for (CElement e : p.getElements()) { aoqi@0: countURI(propUriCountMap, e.getElementName()); aoqi@0: } aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: public Void onAttribute(CAttributePropertyInfo p) { aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: public Void onValue(CValuePropertyInfo p) { aoqi@0: return null; aoqi@0: } aoqi@0: }; aoqi@0: aoqi@0: aoqi@0: for (ClassOutlineImpl co : classes) { aoqi@0: CClassInfo ci = co.target; aoqi@0: countURI(uriCountMap, ci.getTypeName()); aoqi@0: countURI(uriCountMap, ci.getElementName()); aoqi@0: aoqi@0: for( CPropertyInfo p : ci.getProperties() ) aoqi@0: p.accept(propVisitor); aoqi@0: } aoqi@0: mostUsedNamespaceURI = getMostUsedURI(uriCountMap); aoqi@0: aoqi@0: elementFormDefault = getFormDefault(); aoqi@0: attributeFormDefault = XmlNsForm.UNQUALIFIED; aoqi@0: try { aoqi@0: XmlNsForm modelValue = _model.getAttributeFormDefault(mostUsedNamespaceURI); aoqi@0: attributeFormDefault = modelValue; aoqi@0: } catch (Exception e) { aoqi@0: // ignore and accept default aoqi@0: } aoqi@0: aoqi@0: // generate package-info.java aoqi@0: // we won't get this far if the user specified -npa aoqi@0: if(!mostUsedNamespaceURI.equals("") || elementFormDefault==XmlNsForm.QUALIFIED || (attributeFormDefault == XmlNsForm.QUALIFIED)) { aoqi@0: XmlSchemaWriter w = _model.strategy.getPackage(_package, Aspect.IMPLEMENTATION).annotate2(XmlSchemaWriter.class); aoqi@0: if(!mostUsedNamespaceURI.equals("")) aoqi@0: w.namespace(mostUsedNamespaceURI); aoqi@0: if(elementFormDefault==XmlNsForm.QUALIFIED) aoqi@0: w.elementFormDefault(elementFormDefault); aoqi@0: if(attributeFormDefault==XmlNsForm.QUALIFIED) aoqi@0: w.attributeFormDefault(attributeFormDefault); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: // Map to keep track of how often each type or element uri is used in this package aoqi@0: // mostly used to calculate mostUsedNamespaceURI aoqi@0: private HashMap uriCountMap = new HashMap(); aoqi@0: aoqi@0: // Map to keep track of how often each property uri is used in this package aoqi@0: // used to calculate elementFormDefault aoqi@0: private HashMap propUriCountMap = new HashMap(); aoqi@0: aoqi@0: /** aoqi@0: * pull the uri out of the specified QName and keep track of it in the aoqi@0: * specified hash map aoqi@0: * aoqi@0: * @param qname aoqi@0: */ aoqi@0: private void countURI(HashMap map, QName qname) { aoqi@0: if (qname == null) return; aoqi@0: aoqi@0: String uri = qname.getNamespaceURI(); aoqi@0: aoqi@0: if (map.containsKey(uri)) { aoqi@0: map.put(uri, map.get(uri) + 1); aoqi@0: } else { aoqi@0: map.put(uri, 1); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Iterate through the hash map looking for the namespace used aoqi@0: * most frequently. Ties are arbitrarily broken by the order aoqi@0: * in which the map keys are iterated over. aoqi@0: * aoqi@0: *

aoqi@0: * Because JAX-WS often reassigns the "" namespace URI, aoqi@0: * and when that happens it unintentionally also renames (normally aoqi@0: * unqualified) local elements, prefer non-"" URI when there's a tie. aoqi@0: */ aoqi@0: private String getMostUsedURI(HashMap map) { aoqi@0: String mostPopular = null; aoqi@0: int count = 0; aoqi@0: aoqi@0: for (Map.Entry e : map.entrySet()) { aoqi@0: String uri = e.getKey(); aoqi@0: int uriCount = e.getValue(); aoqi@0: if (mostPopular == null) { aoqi@0: mostPopular = uri; aoqi@0: count = uriCount; aoqi@0: } else { aoqi@0: if (uriCount > count || (uriCount==count && mostPopular.equals(""))) { aoqi@0: mostPopular = uri; aoqi@0: count = uriCount; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: if (mostPopular == null) return ""; aoqi@0: return mostPopular; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Calculate the element form defaulting. aoqi@0: * aoqi@0: * Compare the most frequently used property URI to the most frequently used aoqi@0: * element/type URI. If they match, then return QUALIFIED aoqi@0: */ aoqi@0: private XmlNsForm getFormDefault() { aoqi@0: if (getMostUsedURI(propUriCountMap).equals("")) return XmlNsForm.UNQUALIFIED; aoqi@0: else return XmlNsForm.QUALIFIED; aoqi@0: } aoqi@0: aoqi@0: }