/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2015 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package com.sun.tools.xjc.generator.bean; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.xml.bind.annotation.XmlNsForm; import javax.xml.bind.annotation.XmlSchema; import javax.xml.namespace.QName; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JPackage; import com.sun.tools.xjc.generator.annotation.spec.XmlSchemaWriter; import com.sun.tools.xjc.model.CAttributePropertyInfo; import com.sun.tools.xjc.model.CClassInfo; import com.sun.tools.xjc.model.CElement; import com.sun.tools.xjc.model.CElementPropertyInfo; import com.sun.tools.xjc.model.CPropertyInfo; import com.sun.tools.xjc.model.CPropertyVisitor; import com.sun.tools.xjc.model.CReferencePropertyInfo; import com.sun.tools.xjc.model.CTypeRef; import com.sun.tools.xjc.model.CValuePropertyInfo; import com.sun.tools.xjc.model.Model; import com.sun.tools.xjc.outline.PackageOutline; import com.sun.tools.xjc.outline.Aspect; /** * {@link PackageOutline} enhanced with schema2java specific * information. * * @author * Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com), Martin Grebac (martin.grebac@oracle.com) */ public final class PackageOutlineImpl implements PackageOutline { private final Model _model; private final JPackage _package; private final ObjectFactoryGenerator objectFactoryGenerator; /*package*/ final Set<ClassOutlineImpl> classes = new HashSet<ClassOutlineImpl>(); private final Set<ClassOutlineImpl> classesView = Collections.unmodifiableSet(classes); private String mostUsedNamespaceURI; private XmlNsForm elementFormDefault; private XmlNsForm attributeFormDefault; /** * The namespace URI most commonly used in classes in this package. * This should be used as the namespace URI for {@link XmlSchema#namespace()}. * * <p> * Null if no default * * @see #calcDefaultValues() */ public String getMostUsedNamespaceURI() { return mostUsedNamespaceURI; } /** * The attribute form default for this package. * <p> * The value is computed by examining what would yield the smallest generated code. */ public XmlNsForm getAttributeFormDefault() { assert attributeFormDefault!=null; return attributeFormDefault; } /** * The element form default for this package. * <p> * The value is computed by examining what would yield the smallest generated code. */ public XmlNsForm getElementFormDefault() { assert elementFormDefault!=null; return elementFormDefault; } public JPackage _package() { return _package; } public ObjectFactoryGenerator objectFactoryGenerator() { return objectFactoryGenerator; } public Set<ClassOutlineImpl> getClasses() { return classesView; } public JDefinedClass objectFactory() { return objectFactoryGenerator.getObjectFactory(); } protected PackageOutlineImpl( BeanGenerator outline, Model model, JPackage _pkg ) { this._model = model; this._package = _pkg; switch(model.strategy) { case BEAN_ONLY: objectFactoryGenerator = new PublicObjectFactoryGenerator(outline,model,_pkg); break; case INTF_AND_IMPL: objectFactoryGenerator = new DualObjectFactoryGenerator(outline,model,_pkg); break; default: throw new IllegalStateException(); } } /** * Compute the most common namespace URI in this package * (to put into {@link XmlSchema#namespace()} and what value * we should put into {@link XmlSchema#elementFormDefault()}. * * This method is called after {@link #classes} field is filled up. */ public void calcDefaultValues() { // short-circuit if xjc was told not to generate package level annotations in // package-info.java if(!_model.isPackageLevelAnnotations()) { mostUsedNamespaceURI = ""; elementFormDefault = XmlNsForm.UNQUALIFIED; return; } // used to visit properties CPropertyVisitor<Void> propVisitor = new CPropertyVisitor<Void>() { public Void onElement(CElementPropertyInfo p) { for (CTypeRef tr : p.getTypes()) { countURI(propUriCountMap, tr.getTagName()); } return null; } public Void onReference(CReferencePropertyInfo p) { for (CElement e : p.getElements()) { countURI(propUriCountMap, e.getElementName()); } return null; } public Void onAttribute(CAttributePropertyInfo p) { return null; } public Void onValue(CValuePropertyInfo p) { return null; } }; for (ClassOutlineImpl co : classes) { CClassInfo ci = co.target; countURI(uriCountMap, ci.getTypeName()); countURI(uriCountMap, ci.getElementName()); for( CPropertyInfo p : ci.getProperties() ) p.accept(propVisitor); } mostUsedNamespaceURI = getMostUsedURI(uriCountMap); elementFormDefault = getFormDefault(); attributeFormDefault = XmlNsForm.UNQUALIFIED; try { XmlNsForm modelValue = _model.getAttributeFormDefault(mostUsedNamespaceURI); attributeFormDefault = modelValue; } catch (Exception e) { // ignore and accept default } // generate package-info.java // we won't get this far if the user specified -npa if(!mostUsedNamespaceURI.equals("") || elementFormDefault==XmlNsForm.QUALIFIED || (attributeFormDefault == XmlNsForm.QUALIFIED)) { XmlSchemaWriter w = _model.strategy.getPackage(_package, Aspect.IMPLEMENTATION).annotate2(XmlSchemaWriter.class); if(!mostUsedNamespaceURI.equals("")) w.namespace(mostUsedNamespaceURI); if(elementFormDefault==XmlNsForm.QUALIFIED) w.elementFormDefault(elementFormDefault); if(attributeFormDefault==XmlNsForm.QUALIFIED) w.attributeFormDefault(attributeFormDefault); } } // Map to keep track of how often each type or element uri is used in this package // mostly used to calculate mostUsedNamespaceURI private HashMap<String, Integer> uriCountMap = new HashMap<String, Integer>(); // Map to keep track of how often each property uri is used in this package // used to calculate elementFormDefault private HashMap<String, Integer> propUriCountMap = new HashMap<String, Integer>(); /** * pull the uri out of the specified QName and keep track of it in the * specified hash map * * @param qname */ private void countURI(HashMap<String, Integer> map, QName qname) { if (qname == null) return; String uri = qname.getNamespaceURI(); if (map.containsKey(uri)) { map.put(uri, map.get(uri) + 1); } else { map.put(uri, 1); } } /** * Iterate through the hash map looking for the namespace used * most frequently. Ties are arbitrarily broken by the order * in which the map keys are iterated over. * * <p> * Because JAX-WS often reassigns the "" namespace URI, * and when that happens it unintentionally also renames (normally * unqualified) local elements, prefer non-"" URI when there's a tie. */ private String getMostUsedURI(HashMap<String, Integer> map) { String mostPopular = null; int count = 0; for (Map.Entry<String,Integer> e : map.entrySet()) { String uri = e.getKey(); int uriCount = e.getValue(); if (mostPopular == null) { mostPopular = uri; count = uriCount; } else { if (uriCount > count || (uriCount==count && mostPopular.equals(""))) { mostPopular = uri; count = uriCount; } } } if (mostPopular == null) return ""; return mostPopular; } /** * Calculate the element form defaulting. * * Compare the most frequently used property URI to the most frequently used * element/type URI. If they match, then return QUALIFIED */ private XmlNsForm getFormDefault() { if (getMostUsedURI(propUriCountMap).equals("")) return XmlNsForm.UNQUALIFIED; else return XmlNsForm.QUALIFIED; } }