/**
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
* Portions Copyright 2013-2017 Philip Helger + contributors
*
* 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.helger.jcodemodel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.helger.jcodemodel.JTypeWildcard.EBoundMode;
import com.helger.jcodemodel.util.JCValueEnforcer;
/**
* Represents a Java reference type, such as a class, an interface, an enum, an
* array type, a parameterized type.
* <p>
* To be exact, this object represents an "use" of a reference type, not
* necessarily a declaration of it, which is modeled as {@link JDefinedClass}.
*/
public abstract class AbstractJClass extends AbstractJType
{
/**
* Sometimes useful reusable empty array.
*/
public static final JTypeVar [] EMPTY_ARRAY = new JTypeVar [0];
private final JCodeModel m_aOwner;
private JArrayClass m_aArrayClass;
protected AbstractJClass (@Nonnull final JCodeModel aOwner)
{
m_aOwner = JCValueEnforcer.notNull (aOwner, "Owner");
}
/**
* Gets the name of this class.
*
* @return name of this class, without any qualification. For example, this
* method returns "String" for <code>java.lang.String</code>.
*/
@Override
public abstract String name ();
/**
* Gets the package to which this class belongs. TODO: shall we move move this
* down?
*
* @return The {@link JPackage} this class belongs to. Is usually not
* <code>null</code> except for the {@link JTypeVar} and the
* {@link JTypeWildcard} implementation.
*/
public abstract JPackage _package ();
/**
* @return the class in which this class is nested, or <tt>null</tt> if this
* is a top-level class.
*/
@Nullable
public AbstractJClass outer ()
{
return null;
}
@Nonnull
public final JCodeModel owner ()
{
return m_aOwner;
}
/**
* Gets the super class of this class.
*
* @return Returns the {@link AbstractJClass} representing the superclass of
* the entity (class or interface) represented by this
* {@link AbstractJClass}. Even if no super class is given explicitly
* or this {@link AbstractJClass} is not a class, this method still
* returns {@link AbstractJClass} for {@link Object}. If this
* {@link AbstractJClass} represents {@link Object}, return null.
*/
@Nullable
public abstract AbstractJClass _extends ();
/**
* Iterates all super interfaces directly implemented by this class/interface.
*
* @return A non-null valid iterator that iterates all {@link AbstractJClass}
* objects that represents those interfaces implemented by this
* object.
*/
@Nonnull
public abstract Iterator <AbstractJClass> _implements ();
/**
* @return <code>true</code> if this object represents an interface.
*/
public abstract boolean isInterface ();
/**
* @return <code>true</code> if this class is an abstract class.
*/
public abstract boolean isAbstract ();
/**
* @return If this class represents one of the wrapper classes defined in the
* <code>java.lang</code> package, return the corresponding primitive
* type. Otherwise <code>null</code>.
*/
@Nullable
public JPrimitiveType getPrimitiveType ()
{
return null;
}
/**
* @deprecated calling this method from {@link AbstractJClass} would be
* meaningless, since it's always guaranteed to return
* <tt>this</tt>.
*/
@Deprecated
@Override
public AbstractJClass boxify ()
{
return this;
}
@Override
@Nonnull
public AbstractJType unboxify ()
{
final JPrimitiveType pt = getPrimitiveType ();
return pt == null ? this : pt;
}
@Override
@Nonnull
public AbstractJClass erasure ()
{
return this;
}
/**
* Gets the parameterization of the given base type.
* <p>
* For example, given the following
*
* <pre>
* <code>
* interface Foo<T> extends List<List<T>> {}
* interface Bar extends Foo<String> {}
* </code>
* </pre>
*
* This method works like this:
*
* <pre>
* <code>
* getBaseClass( Bar, List ) = List<List<String>
* getBaseClass( Bar, Foo ) = Foo<String>
* getBaseClass( Foo<? extends Number>, Collection ) = Collection<List<? extends Number>>
* getBaseClass( ArrayList<? extends BigInteger>, List ) = List<? extends BigInteger>
* </code>
* </pre>
*
* @param baseType
* The class whose parameterization we are interested in.
* @return The use of {@code baseType} in {@code this} type. or null if the
* type is not assignable to the base type.
*/
@Nullable
public final AbstractJClass getBaseClass (@Nonnull final AbstractJClass baseType)
{
if (erasure ().equals (baseType))
return this;
final AbstractJClass b = _extends ();
if (b != null)
{
final AbstractJClass bc = b.getBaseClass (baseType);
if (bc != null)
return bc;
}
final Iterator <AbstractJClass> itfs = _implements ();
while (itfs.hasNext ())
{
final AbstractJClass bc = itfs.next ().getBaseClass (baseType);
if (bc != null)
return bc;
}
return null;
}
@Nullable
public final AbstractJClass getBaseClass (@Nonnull final Class <?> baseType)
{
return getBaseClass (owner ().ref (baseType));
}
@Override
@Nonnull
public JArrayClass array ()
{
if (m_aArrayClass == null)
m_aArrayClass = new JArrayClass (owner (), this);
return m_aArrayClass;
}
/**
* "Narrows" a generic class to a concrete class by specifying a type
* argument.<br>
* <code>.narrow(X)</code> builds <code>Set<X></code> from
* <code>Set</code>.
*
* @param clazz
* class to narrow with
* @return Never <code>null</code>. Narrowed class.
*/
@Nonnull
public JNarrowedClass narrow (@Nonnull final Class <?> clazz)
{
return narrow (owner ().ref (clazz));
}
@Nonnull
public AbstractJClass narrow (@Nonnull final Class <?>... clazz)
{
final AbstractJClass [] r = new AbstractJClass [clazz.length];
for (int i = 0; i < clazz.length; i++)
r[i] = owner ().ref (clazz[i]);
return narrow (r);
}
/**
* "Narrows" a generic class to a concrete class by specifying a type
* argument. <br>
* <code>.narrow(X)</code> builds <code>Set<X></code> from
* <code>Set</code>.
*
* @param clazz
* class to narrow with
* @return Never <code>null</code>. Narrowed class.
*/
@Nonnull
public JNarrowedClass narrow (final AbstractJClass clazz)
{
return new JNarrowedClass (this, clazz);
}
@Nonnull
public JNarrowedClass narrow (@Nonnull final AbstractJType type)
{
return narrow (type.boxify ());
}
@Nonnull
public AbstractJClass narrow (@Nonnull final AbstractJClass... clazz)
{
return new JNarrowedClass (this, Arrays.asList (clazz.clone ()));
}
@Nonnull
public AbstractJClass narrow (@Nonnull final List <? extends AbstractJClass> clazz)
{
return new JNarrowedClass (this, new ArrayList <AbstractJClass> (clazz));
}
/**
* @return A narrowed type without any type parameter (as in
* <code>HashMap<></code>)
*/
@Nonnull
public AbstractJClass narrowEmpty ()
{
return new JNarrowedClass (this, new ArrayList <AbstractJClass> ());
}
/**
* @return If this class is parameterized, the type parameters of the given
* index.
*/
@Nonnull
public List <? extends AbstractJClass> getTypeParameters ()
{
return Collections.emptyList ();
}
/**
* Iterates all the type parameters of this class/interface. <br>
* For example, if this {@link AbstractJClass} represents
* <code>Set<T></code>, this method returns an array that contains
* single {@link JTypeVar} for 'T'.
*
* @return All type parameters as array.
*/
@Nonnull
public JTypeVar [] typeParams ()
{
return EMPTY_ARRAY;
}
/**
* @return <code>true</code> if this class is a parameterized class.
*/
public final boolean isParameterized ()
{
return erasure () != this;
}
/**
* Create "? extends T" from T.
*
* @return never <code>null</code>
*/
@Nonnull
public final JTypeWildcard wildcard ()
{
return wildcard (EBoundMode.EXTENDS);
}
/**
* Create "? super T" from T.
*
* @return never <code>null</code>
*/
@Nonnull
public final JTypeWildcard wildcardSuper ()
{
return wildcard (EBoundMode.SUPER);
}
/**
* Create "? extends T" from T or "? super T" from T.
*
* @param eMode
* "extends" or "super"
* @return never <code>null</code>
*/
@Nonnull
public final JTypeWildcard wildcard (@Nonnull final EBoundMode eMode)
{
return new JTypeWildcard (this, eMode);
}
/**
* Substitutes the type variables with their actual arguments. <br>
* For example, when this class is Map<String,Map<V>>, (where V
* then doing substituteParams( V, Integer ) returns a {@link AbstractJClass}
* for <code>Map<String,Map<Integer>></code>. <br>
* This method needs to work recursively.
*
* @param variables
* Type variables
* @param bindings
* Bindings
* @return Never <code>null</code>.
*/
@Nonnull
protected abstract AbstractJClass substituteParams (@Nonnull JTypeVar [] variables,
@Nonnull List <? extends AbstractJClass> bindings);
/**
* @return name<code>.class</code>
*/
@Nonnull
public final IJExpression dotclass ()
{
return JExpr.dotclass (this);
}
/**
* Generates a static method invocation.
*
* @param aMethod
* Method to be invoked
* @return Newly created {@link JInvocation}
*/
@Nonnull
public final JInvocation staticInvoke (@Nonnull final JMethod aMethod)
{
return new JInvocation (this, aMethod);
}
/**
* Generates a static method invocation.
*
* @param sMethod
* Method to be invoked
* @return Newly created {@link JInvocation}
*/
@Nonnull
public final JInvocation staticInvoke (@Nonnull final String sMethod)
{
return new JInvocation (this, sMethod);
}
/**
* Static field reference.
*
* @param sField
* Field to be referenced
* @return Newly created {@link JFieldRef}
*/
@Nonnull
public final JFieldRef staticRef (final String sField)
{
return new JFieldRef (this, sField);
}
/**
* Static field reference.
*
* @param aField
* Field to be referenced
* @return Newly created {@link JFieldRef}
*/
@Nonnull
public final JFieldRef staticRef (final JVar aField)
{
return new JFieldRef (this, aField);
}
/**
* Method reference for JDK8 (as in <code>String::valueOf</code>).
*
* @param sMethod
* Method to be referenced
* @return Newly created {@link JLambdaMethodRef}
*/
@Nonnull
public final JLambdaMethodRef methodRef (@Nonnull final String sMethod)
{
return new JLambdaMethodRef (this, sMethod);
}
public void generate (@Nonnull final JFormatter f)
{
f.type (this);
}
/**
* Prints the class name in javadoc @link format.
*
* @param f
* Formatter to be used
*/
void printLink (@Nonnull final JFormatter f)
{
f.print ("{@link ").generable (this).print ('}');
}
@Override
public String toString ()
{
return getClass ().getName () + '(' + fullName () + ')';
}
}