com.sun.codemodel.JPackage Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 1997, 2022 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package com.sun.codemodel;
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.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;
import java.util.Collection;
import java.util.Collections;
/**
* A Java package.
*/
public final class JPackage implements JDeclaration, JGenerable, JClassContainer, JAnnotatable, Comparable, JDocCommentable {
/**
* Name of the package.
* May be the empty string for the root package.
*/
private String name;
private final JCodeModel owner;
/**
* List of classes contained within this package keyed by their name.
*/
private final Map classes = new TreeMap<>();
/**
* List of resources files inside this package.
*/
private final Set resources = new HashSet<>();
/**
* All {@link JClass}s in this package keyed the upper case class name.
*
* This field is non-null only on Windows, to detect
* "Foo" and "foo" as a collision.
*/
private final Map upperCaseClassMap;
/**
* Lazily created list of package annotations.
*/
private List annotations = null;
/**
* package javadoc.
*/
private JDocComment jdoc = null;
/**
* JPackage 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
*/
JPackage(String name, JCodeModel cw) {
this.owner = cw;
if (name.equals(".")) {
String msg = "Package name . is not allowed";
throw new IllegalArgumentException(msg);
}
if(JCodeModel.isCaseSensitiveFileSystem)
upperCaseClassMap = null;
else
upperCaseClassMap = new HashMap<>();
this.name = name;
}
@Override
public JClassContainer parentContainer() {
return parent();
}
/**
* Gets the parent package, or null if this class is the root package.
*/
public JPackage parent() {
if(name.length()==0) return null;
int idx = name.lastIndexOf('.');
return owner._package(name.substring(0,idx));
}
@Override
public boolean isClass() { return false; }
@Override
public boolean isPackage() { return true; }
@Override
public JPackage 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
*
* @exception JClassAlreadyExistsException
* When the specified class/interface was already created.
*/
@Override
public JDefinedClass _class(int mods, String name) throws JClassAlreadyExistsException {
return _class(mods,name,ClassType.CLASS);
}
/**
*
* @deprecated
*/
@Deprecated
@Override
public JDefinedClass _class( int mods, String name, boolean isInterface ) throws JClassAlreadyExistsException {
return _class(mods,name, isInterface?ClassType.INTERFACE:ClassType.CLASS );
}
@Override
public JDefinedClass _class( int mods, String name, ClassType classTypeVal ) throws JClassAlreadyExistsException {
if(classes.containsKey(name))
throw new JClassAlreadyExistsException(classes.get(name));
else {
// XXX problems caught in the NC constructor
JDefinedClass c = new JDefinedClass(this, mods, name, classTypeVal);
if( upperCaseClassMap!=null ) {
JDefinedClass dc = upperCaseClassMap.get(name.toUpperCase());
if(dc!=null)
throw new JClassAlreadyExistsException(dc);
upperCaseClassMap.put(name.toUpperCase(),c);
}
classes.put(name,c);
return c;
}
}
/**
* Adds a public class to this package.
*
*/
@Override
public JDefinedClass _class(String name) throws JClassAlreadyExistsException {
return _class(JMod.PUBLIC, name);
}
/**
* Gets a reference to the already created {@link JDefinedClass}.
*
* @return null
* If the class is not yet created.
*/
public JDefinedClass _getClass(String name) {
return classes.getOrDefault(name, null);
}
/**
* Order is based on the lexicological order of the package name.
*/
@Override
public int compareTo(JPackage 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
*/
@Override
public JDefinedClass _interface(int mods, String name) throws JClassAlreadyExistsException {
return _class(mods,name,ClassType.INTERFACE);
}
/**
* Adds a public interface to this package.
*/
@Override
public JDefinedClass _interface(String name) throws JClassAlreadyExistsException {
return _interface(JMod.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
* @exception JClassAlreadyExistsException
* When the specified class/interface was already created.
*/
@Override
public JDefinedClass _annotationTypeDeclaration(String name) throws JClassAlreadyExistsException {
return _class (JMod.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
* @exception JClassAlreadyExistsException
* When the specified class/interface was already created.
*/
@Override
public JDefinedClass _enum (String name) throws JClassAlreadyExistsException {
return _class (JMod.PUBLIC,name,ClassType.ENUM);
}
/**
* Adds a new resource file to this package.
*/
public JResourceFile addResourceFile(JResourceFile rsrc) {
resources.add(rsrc);
return rsrc;
}
/**
* Checks if a resource of the given name exists.
*/
public boolean hasResourceFile(String name) {
for (JResourceFile r : resources)
if (r.name().equals(name))
return true;
return false;
}
/**
* Iterates all resource files in this package.
*/
public Iterator propertyFiles() {
return resources.iterator();
}
/**
* Creates, if necessary, and returns the package javadoc for this
* JDefinedClass.
*
* @return JDocComment containing javadocs for this class
*/
@Override
public JDocComment javadoc() {
if (jdoc == null)
jdoc = new JDocComment(owner());
return jdoc;
}
/**
* Removes a class from this package.
*/
public void remove(JClass 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 JClass ref(String name) throws ClassNotFoundException {
if (name.indexOf('.') >= 0)
throw new IllegalArgumentException("JClass 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 JPackage 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.
*/
@Override
public Iterator classes() {
return classes.values().iterator();
}
/**
* Checks if this package contains any classes.
* @return {@code true} if this package contains any classes
* or {@code false} otherwise.
*/
public boolean hasClasses() {
return !classes.isEmpty();
}
/**
* Checks if a given name is already defined as a class/interface
*/
public boolean isDefined(String classLocalName) {
Iterator 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 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
* "java.lang"
*/
public String name() {
return name;
}
/**
* Return the code model root object being used to create this package.
*/
@Override
public JCodeModel owner() { return owner; }
@Override
public JAnnotationUse annotate(JClass clazz) {
if(isUnnamed())
throw new IllegalArgumentException("the root package cannot be annotated");
if(annotations==null)
annotations = new ArrayList<>();
JAnnotationUse a = new JAnnotationUse(clazz);
annotations.add(a);
return a;
}
@Override
public JAnnotationUse annotate(Class extends Annotation> clazz) {
return annotate(owner.ref(clazz));
}
@Override
public > W annotate2(Class clazz) {
return TypedAnnotationWriter.create(clazz,this);
}
@Override
public boolean removeAnnotation(JAnnotationUse annotation) {
return this.annotations.remove(annotation);
}
@Override
public Collection annotations() {
if (annotations == null)
annotations = new ArrayList<>();
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));
}
@Override
public void declare(JFormatter f ) {
if (name.length() != 0)
f.p("package").p(name).p(';').nl();
}
@Override
public void generate(JFormatter f) {
f.p(name);
}
void build( CodeWriter src, CodeWriter res ) throws IOException {
// write classes
for (JDefinedClass c : classes.values()) {
if (c.isHidden())
continue; // don't generate this file
JFormatter f = createJavaSourceFileWriter(src, c.name());
f.write(c);
f.close();
}
// write package annotations
if ((annotations!=null && !annotations.isEmpty()) || jdoc != null) {
JFormatter f = createJavaSourceFileWriter(src,"package-info");
if (jdoc != null)
f.g(jdoc);
// TODO: think about importing
if (annotations != null){
for (JAnnotationUse a : annotations)
f.g(a).nl();
}
f.d(this);
f.close();
}
// write resources
for (JResourceFile 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 (JDefinedClass c : classes.values()) {
if (c.isHidden())
continue; // don't generate this file
r++;
}
if ((annotations!=null && !annotations.isEmpty()) || jdoc != null) {
r++;
}
r+= resources.size();
return r;
}
private JFormatter createJavaSourceFileWriter(CodeWriter src, String className) throws IOException {
Writer bw = new BufferedWriter(src.openSource(this,className+".java"));
return new JFormatter(new PrintWriter(bw), owner.classNameReplacer());
}
}