/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2010 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 org.mule.devkit.model.code; import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.io.Writer; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; /** * A Java package. */ public final class Package implements Declaration, Generable, ClassContainer, Annotable, Comparable<Package>, DocCommentable { /** * Name of the package. * May be the empty string for the root package. */ private String name; private final CodeModel owner; /** * List of classes contained within this package keyed by their name. */ private final Map<String, DefinedClass> classes = new TreeMap<String, DefinedClass>(); /** * List of resources files inside this package. */ private final Set<ResourceFile> resources = new HashSet<ResourceFile>(); /** * All {@link TypeReference}s in this package keyed the upper case class name. * <p/> * This field is non-null only on Windows, to detect * "Foo" and "foo" as a collision. */ private final Map<String, DefinedClass> upperCaseClassMap; /** * Lazily created list of package annotations. */ private List<AnnotationUse> annotations = null; /** * package javadoc. */ private DocComment jdoc = null; /** * Package constructor * * @param name Name of package * @param cw The code writer being used to create this package * @throws IllegalArgumentException If each part of the package name is not a valid identifier */ Package(String name, CodeModel cw) { this.owner = cw; if (name.equals(".")) { String msg = "Package name . is not allowed"; throw new IllegalArgumentException(msg); } if (CodeModel.isCaseSensitiveFileSystem) { upperCaseClassMap = null; } else { upperCaseClassMap = new HashMap<String, DefinedClass>(); } this.name = name; } public ClassContainer parentContainer() { return parent(); } /** * Gets the parent package, or null if this class is the root package. */ public Package parent() { if (name.length() == 0) { return null; } int idx = name.lastIndexOf('.'); return owner._package(name.substring(0, idx)); } public boolean isClass() { return false; } public boolean isPackage() { return true; } public Package getPackage() { return this; } /** * Add a class to this package. * * @param mods Modifiers for this class declaration * @param name Name of class to be added to this package * @return Newly generated class * @throws ClassAlreadyExistsException When the specified class/interface was already created. */ public DefinedClass _class(int mods, String name) throws ClassAlreadyExistsException { return _class(mods, name, ClassType.CLASS); } /** * {@inheritDoc} * * @deprecated */ public DefinedClass _class(int mods, String name, boolean isInterface) throws ClassAlreadyExistsException { return _class(mods, name, isInterface ? ClassType.INTERFACE : ClassType.CLASS); } public DefinedClass _class(int mods, String name, ClassType classTypeVal) throws ClassAlreadyExistsException { if (classes.containsKey(name)) { throw new ClassAlreadyExistsException(classes.get(name)); } else { // XXX problems caught in the NC constructor DefinedClass c = new DefinedClass(this, mods, name, classTypeVal); if (upperCaseClassMap != null) { DefinedClass dc = upperCaseClassMap.get(name.toUpperCase()); if (dc != null) { throw new ClassAlreadyExistsException(dc); } upperCaseClassMap.put(name.toUpperCase(), c); } classes.put(name, c); return c; } } /** * Adds a public class to this package. */ public DefinedClass _class(String name) { try { return _class(Modifier.PUBLIC, name); } catch (ClassAlreadyExistsException ee) { return ee.getExistingClass(); } } /** * Adds a public class to this package. */ public DefinedClass _class(String name, TypeReference _extends) { return _class(Modifier.PUBLIC, name, _extends); } /** * Adds a public class to this package. */ public DefinedClass _class(int modifiers, String name, TypeReference _extends) { try { DefinedClass clazz = _class(modifiers, name); clazz._extends(_extends); return clazz; } catch (ClassAlreadyExistsException ee) { return ee.getExistingClass(); } } /** * Adds a public class to this package. */ public DefinedClass _class(String name, Class<?> _extends) { try { DefinedClass clazz = _class(Modifier.PUBLIC, name); clazz._extends(_extends); return clazz; } catch (ClassAlreadyExistsException ee) { return ee.getExistingClass(); } } /** * Adds a public class to this package. */ public DefinedClass _class(String name, Class<?> _extends, Class<?>[] _implements) { try { DefinedClass clazz = _class(Modifier.PUBLIC, name); clazz._extends(_extends); for (Class<?> _implement : _implements) { clazz._implements(_implement); } return clazz; } catch (ClassAlreadyExistsException ee) { return ee.getExistingClass(); } } /** * Adds a public class to this package. */ public DefinedClass _class(String name, Class<?>[] _implements) { try { DefinedClass clazz = _class(Modifier.PUBLIC, name); for (Class<?> _implement : _implements) { clazz._implements(_implement); } return clazz; } catch (ClassAlreadyExistsException ee) { return ee.getExistingClass(); } } /** * Gets a reference to the already created {@link DefinedClass}. * * @return null * If the class is not yet created. */ public DefinedClass _getClass(String name) { if (classes.containsKey(name)) { return classes.get(name); } else { return null; } } /** * Order is based on the lexicological order of the package name. */ public int compareTo(Package that) { return this.name.compareTo(that.name); } /** * Add an interface to this package. * * @param mods Modifiers for this interface declaration * @param name Name of interface to be added to this package * @return Newly generated interface */ public DefinedClass _interface(int mods, String name) throws ClassAlreadyExistsException { return _class(mods, name, ClassType.INTERFACE); } /** * Adds a public interface to this package. */ public DefinedClass _interface(String name) throws ClassAlreadyExistsException { return _interface(Modifier.PUBLIC, name); } /** * Add an annotationType Declaration to this package * * @param name Name of the annotation Type declaration to be added to this package * @return newly created Annotation Type Declaration * @throws ClassAlreadyExistsException When the specified class/interface was already created. */ public DefinedClass _annotationTypeDeclaration(String name) throws ClassAlreadyExistsException { return _class(Modifier.PUBLIC, name, ClassType.ANNOTATION_TYPE_DECL); } /** * Add a public enum to this package * * @param name Name of the enum to be added to this package * @return newly created Enum * @throws ClassAlreadyExistsException When the specified class/interface was already created. */ public DefinedClass _enum(String name) throws ClassAlreadyExistsException { return _class(Modifier.PUBLIC, name, ClassType.ENUM); } /** * Adds a new resource file to this package. */ public ResourceFile addResourceFile(ResourceFile rsrc) { resources.add(rsrc); return rsrc; } /** * Checks if a resource of the given name exists. */ public boolean hasResourceFile(String name) { for (ResourceFile r : resources) { if (r.name().equals(name)) { return true; } } return false; } /** * Iterates all resource files in this package. */ public Iterator<ResourceFile> propertyFiles() { return resources.iterator(); } /** * Creates, if necessary, and returns the package javadoc for this * DefinedClass. * * @return JDocComment containing javadocs for this class */ public DocComment javadoc() { if (jdoc == null) { jdoc = new DocComment(owner()); } return jdoc; } /** * Removes a class from this package. */ public void remove(TypeReference c) { if (c._package() != this) { throw new IllegalArgumentException( "the specified class is not a member of this package," + " or it is a referenced class"); } // note that c may not be a member of classes. // this happens when someone is trying to remove a non generated class classes.remove(c.name()); if (upperCaseClassMap != null) { upperCaseClassMap.remove(c.name().toUpperCase()); } } /** * Reference a class within this package. */ public TypeReference ref(String name) throws ClassNotFoundException { if (name.indexOf('.') >= 0) { throw new IllegalArgumentException("TypeReference name contains '.': " + name); } String n = ""; if (!isUnnamed()) { n = this.name + '.'; } n += name; return owner.ref(Class.forName(n)); } /** * Gets a reference to a sub package of this package. */ public Package subPackage(String pkg) { if (isUnnamed()) { return owner()._package(pkg); } else { return owner()._package(name + '.' + pkg); } } /** * Returns an iterator that walks the top-level classes defined in this * package. */ public Iterator<DefinedClass> classes() { return classes.values().iterator(); } /** * Checks if a given name is already defined as a class/interface */ public boolean isDefined(String classLocalName) { Iterator<DefinedClass> itr = classes(); while (itr.hasNext()) { if ((itr.next()).name().equals(classLocalName)) { return true; } } return false; } /** * Checks if this package is the root, unnamed package. */ public final boolean isUnnamed() { return name.length() == 0; } /** * Get the name of this package * * @return The name of this package, or the empty string if this is the * null package. For example, this method returns strings like * <code>"java.lang"</code> */ public String name() { return name; } /** * Return the code model root object being used to create this package. */ public final CodeModel owner() { return owner; } public AnnotationUse annotate(TypeReference clazz) { if (isUnnamed()) { throw new IllegalArgumentException("the root package cannot be annotated"); } if (annotations == null) { annotations = new ArrayList<AnnotationUse>(); } AnnotationUse a = new AnnotationUse(clazz); annotations.add(a); return a; } public AnnotationUse annotate(Class<? extends Annotation> clazz) { return annotate(owner.ref(clazz)); } public <W extends AnnotationWriter> W annotate2(Class<W> clazz) { return TypedAnnotationWriter.create(clazz, this); } public Collection<AnnotationUse> annotations() { if (annotations == null) { annotations = new ArrayList<AnnotationUse>(); } return Collections.unmodifiableList(annotations); } /** * Convert the package name to directory path equivalent */ File toPath(File dir) { if (name == null) { return dir; } return new File(dir, name.replace('.', File.separatorChar)); } public void declare(Formatter f) { if (name.length() != 0) { f.p("package").p(name).p(';').nl(); } } public void generate(Formatter f) { f.p(name); } void build(CodeWriter src, CodeWriter res) throws IOException { // write classes for (DefinedClass c : classes.values()) { if (c.isHidden()) { continue; // don't generate this file } Formatter f = createJavaSourceFileWriter(src, c.name()); f.write(c); f.close(); } // write package annotations if (annotations != null || jdoc != null) { Formatter f = createJavaSourceFileWriter(src, "package-info"); if (jdoc != null) { f.g(jdoc); } // TODO: think about importing if (annotations != null) { for (AnnotationUse a : annotations) { f.g(a).nl(); } } f.d(this); f.close(); } // write resources for (ResourceFile rsrc : resources) { CodeWriter cw = rsrc.isResource() ? res : src; OutputStream os = new BufferedOutputStream(cw.openBinary(this, rsrc.name())); rsrc.build(os); os.close(); } } /*package*/ int countArtifacts() { int r = 0; for (DefinedClass c : classes.values()) { if (c.isHidden()) { continue; // don't generate this file } r++; } if (annotations != null || jdoc != null) { r++; } r += resources.size(); return r; } private Formatter createJavaSourceFileWriter(CodeWriter src, String className) throws IOException { Writer bw = new BufferedWriter(src.openSource(this, className + ".java")); return new Formatter(new PrintWriter(bw)); } }