blob: 4860f43282d8a77536d82d45f05704fcecf9f71a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jst.j2ee.internal.java.codegen;
import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IOpenable;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.jdom.IDOMType;
import org.eclipse.jst.j2ee.internal.codegen.GenerationException;
import org.eclipse.jst.j2ee.internal.codegen.IBaseGenConstants;
import org.eclipse.jst.j2ee.internal.codegen.IGenerationBuffer;
/**
* A Java type generator is used to create or update a Java class or interface.
*/
public abstract class JavaTypeGenerator extends JavaMemberGenerator {
private boolean fPrepared = false;
static final String EXTENDS = " extends ";//$NON-NLS-1$
static final String BEGIN_BRACE = "{"; //$NON-NLS-1$
static final String END_BRACE = "}"; //$NON-NLS-1$
/**
* JavaTypeGenerator default constructor.
*/
public JavaTypeGenerator() {
super();
}
protected MergeResults calculateMergeResults(JavaMemberHistoryDescriptor memberHistory, JavaMemberDescriptor desc) throws GenerationException {
return calculateMergeResults(memberHistory, (JavaTypeDescriptor) desc);
}
/**
* Compares the type history and the new type description to arrive at the needed actions
* described by the merge results.
*/
protected MergeResults calculateMergeResults(JavaMemberHistoryDescriptor typeHistory, JavaTypeDescriptor desc) throws GenerationException {
// If not remove only, get the new type contents.
String source = null;
IDOMType newType = null;
if (!typeHistory.isDeleteOnly()) {
IGenerationBuffer typeBuf = getGenerationBuffer();
generateType(desc, typeBuf, (typeHistory.getCollisionMember() == null));
source = typeBuf.toString();
newType = getDOMFactory().createType(source);
}
// Check for merging and deletes. If merged, get the updated contents.
MergeResults mr = dispatchToMergeStrategy(typeHistory, newType);
if (mr.isMerged())
source = mr.getSource();
mr.setSource(source);
return mr;
}
/**
* Creates a history descriptor using the same properties that are used to describe the new
* type. That is, the default history descriptor indicates that the old member and the collision
* member are the same.
*
* @see JavaMemberHistoryDescriptor
*/
protected final JavaMemberHistoryDescriptor createDefaultHistoryDescriptor(JavaMemberDescriptor desc) throws GenerationException {
JavaMemberHistoryDescriptor historyDesc = new JavaMemberHistoryDescriptor();
historyDesc.setName(desc.getName());
getMatchingMember(historyDesc);
historyDesc.setCollisionMember(historyDesc.getOldMember());
return historyDesc;
}
/**
* If no history descriptor is set,
* {@link JavaTypeGenerator#createDefaultHistoryDescriptor(JavaMemberDescriptor)}is used to
* create one. Otherwise, the existing history descriptor's old and collision members are set
* appropriately.
*
* @see JavaMemberHistoryDescriptor
*/
protected JavaMemberHistoryDescriptor createHistoryDescriptor(JavaMemberDescriptor desc) throws GenerationException {
JavaMemberHistoryDescriptor historyDesc = getHistoryDescriptor();
if (historyDesc == null) {
historyDesc = createDefaultHistoryDescriptor(desc);
} else {
getMatchingMember(historyDesc);
if (!historyDesc.isDeleteOnly()) {
if (historyDesc.getName().equals(desc.getName())) {
historyDesc.setCollisionMember(historyDesc.getOldMember());
} else {
// Renames of main types are only supported in the framework
// via a compilation unit rename.
IType oldType = (IType) historyDesc.getOldMember();
if (!isInner() && (oldType != null)) {
ICompilationUnit oldCU = oldType.getCompilationUnit();
String oldCUMainTypeName = oldCU.getElementName();
oldCUMainTypeName = oldCUMainTypeName.substring(0, oldCUMainTypeName.length() - 5);
if (oldType.getElementName().equals(oldCUMainTypeName))
throw new GenerationException(JavaCodeGenResourceHandler.getString("Renames_of_main_types_are__EXC_")); //$NON-NLS-1$ = "Renames of main types are only supported in the Java code generation framework via a compilation unit rename."
}
// Other type renames can be done this way.
historyDesc.setCollisionMember(createDefaultHistoryDescriptor(desc).getCollisionMember());
}
}
}
return historyDesc;
}
/**
* Override of superclass signature redirects to derived implementation.
*/
protected JavaMemberDescriptor createMemberDescriptor() throws GenerationException {
return createTypeDescriptor();
}
/**
* Creates the type descriptor. Subclasses generally do not need to override this unless doing
* so will save significant time.
*/
protected abstract JavaTypeDescriptor createTypeDescriptor() throws GenerationException;
/**
* Makes a top level type in a compilation unit.
*/
private IType createTypeInCU(String source, IJavaElement sibling) throws GenerationException {
ICompilationUnit compilationUnit = getCompilationUnit();
IType type = null;
try {
type = compilationUnit.createType(source, sibling, false, getTargetContext().getProgressMonitor());
} catch (JavaModelException exc) {
throw new GenerationException(getExceptionIndicator(exc), exc);
}
return type;
}
/**
* Makes an inner type inside another type.
*/
private IType createTypeInType(String source, IJavaElement sibling) throws GenerationException {
IType outerType = getDeclaringType();
IType type = null;
try {
type = outerType.createType(source, sibling, false, getTargetContext().getProgressMonitor());
} catch (JavaModelException exc) {
throw new GenerationException(getExceptionIndicator(exc), exc);
}
return type;
}
private String getExceptionIndicator(JavaModelException e) {
StringBuffer b = new StringBuffer();
try {
b.append(getName());
b.append(" :: "); //$NON-NLS$ //$NON-NLS-1$
} catch (GenerationException ge) {
}
b.append(e.getMessage());
return b.toString();
}
/**
* Dispatch to the merge strategy. This method is here simply to provide a hook for derived
* generators. For example, a generator could override this method to use a specialized merglet
* rather than the default merglet for the merge strategy.
*
* @see IJavaMergeStrategy
* @see IJavaMerglet
*/
protected MergeResults dispatchToMergeStrategy(JavaMemberHistoryDescriptor typeHistory, IDOMType newType) throws GenerationException {
return getCompilationUnitGenerator().getMergeStrategy().merge(typeHistory, newType);
}
/**
* Generates the new type.
*/
void generateTarget(IJavaElement sibling, MergeResults mr) throws GenerationException {
// Create the type.
IType type = null;
if (isInner())
type = createTypeInType(mr.getSource(), sibling);
else
type = createTypeInCU(mr.getSource(), sibling);
// The type is this generator's Java element.
setTargetElement(type);
}
/**
* Populates the generation buffer with the basic class definition.
*/
protected void generateType(JavaTypeDescriptor desc, IGenerationBuffer typeBuf, boolean isNew) throws GenerationException {
// Comment and start the declaration.
if (isNew) // Blank line to start for new types.
typeBuf.append(IJavaGenConstants.LINE_SEPARATOR);
appendComment(typeBuf, desc.getComment());
String temp = formatFlags(desc.getFlags());
if ((temp != null) && (temp.length() > 0)) {
typeBuf.appendWithMargin(temp);
typeBuf.append(IBaseGenConstants.SPACE);
} else {
typeBuf.margin();
}
// Do the class/interface specific declaration bit.
primTypeKindDecl(desc, typeBuf);
// Put the body in the buffer.
typeBuf.append(IJavaGenConstants.MEMBER_CONTENT_START);
getBody(typeBuf);
typeBuf.appendWithMargin(IJavaGenConstants.MEMBER_CONTENT_END);
}
/**
* This implementation returns null for an empty body. Subclasses can override this method to
* return the type body. Alternatively, the subclass can create a set of set of dependent
* generators (in initialize or analyze) and they will be run in getBody(IGenerationBuffer).
* <p>
* However, the typical usage is to have child base generators the the members of the type (see
* {@link JavaMemberGenerator}and it's subclasses).
*/
protected String getBody() throws GenerationException {
return null;
}
/**
* This implementation indents, and checks for a "String getBody()" override. If there was an
* override, its result is put in the buffer with margin. If there was not, the dependent
* children are run to create the body. This implementation then unindents.
*/
protected void getBody(IGenerationBuffer bodyBuf) throws GenerationException {
bodyBuf.indent();
String body = getBody();
if (body == null)
runDependents(bodyBuf);
else
bodyBuf.appendWithMargin(body);
bodyBuf.unindent();
}
/**
* Returns the Javadoc comment for the member. The default is the method name. May get marked
* generated. Subclasses will usually want to override. Overrides just need to return the
* description. The framework will handle adding the Javadoc comment delimiters.
*/
protected String getComment() throws GenerationException {
return getName() + IJavaGenConstants.LINE_SEPARATOR;
}
/**
* Returns the enclosing compilation unit for the type. The compilation unit is prepared only if
* it is created new or has previously been prepared.
*/
private ICompilationUnit getCompilationUnit() throws GenerationException {
return getCompilationUnitGenerator().getCompilationUnit();
}
/**
* Override to return null for top level types.
*/
protected IType getDeclaringType() throws GenerationException {
IType declType = null;
if (isInner())
declType = super.getDeclaringType();
return declType;
}
/**
* Returns the type generator for enclosing type. Returns null for a top level type.
*
* @exception GenerationException
* if the declaring type generator can not be found.
*/
public JavaTypeGenerator getDeclaringTypeGenerator() throws GenerationException {
JavaTypeGenerator declTypeGen = null;
if (isInner())
declTypeGen = super.getDeclaringTypeGenerator();
return declTypeGen;
}
/**
* Looks for a matching method in the declaring type or compilation unit for top level types. If
* one is found, the old member of the member history is set to it. If one is not found, the old
* member of the member history is set to null.
*/
protected void getMatchingMember(JavaMemberHistoryDescriptor memberHistory) throws GenerationException {
IType type = null;
if (isInner()) {
IType declType = getDeclaringType();
if (declType != null)
type = declType.getType(memberHistory.getName());
} else {
ICompilationUnit cu = getCompilationUnit();
if (cu != null)
type = cu.getType(memberHistory.getName());
}
if ((type != null) && (type.exists()))
memberHistory.setOldMember(type);
else
memberHistory.setOldMember(null);
}
/**
* Subclasses must implement to get the list of super interfaces. The default is no super
* interfaces.
*/
protected String[] getSuperInterfaceNames() throws GenerationException {
return null;
}
/**
* Returns the type that is being generated. The type is prepared only if it was previously
* prepared or was created new. If an unprepared type is accessed and the generator is
* subsequently prepared, the declaring type must be reaccessed. Must only be called by child
* generators during run().
*/
IType getType() throws GenerationException {
IType type = (IType) getTargetElement();
if (type == null) {
if (isInner()) {
IType declType = getDeclaringType();
if (declType != null)
type = declType.getType(getName().trim());
} else {
ICompilationUnit cu = getCompilationUnit();
if (cu != null)
type = cu.getType(getName().trim());
}
if ((type != null) && (type.exists()))
setTargetElement(type);
}
return (IType) getTargetElement();
}
/**
* Returns true if the generated type is a class.
*
* @return boolean
*/
public boolean isClass() {
return false;
}
/**
* Returns true if a working copy of the enclosing compilation unit has been obtained.
*
* @see JavaCompilationUnitGenerator#isPrepared()
*/
public boolean isCompilationUnitPrepared() throws GenerationException {
return getCompilationUnitGenerator().isPrepared();
}
/**
* Returns true if the generated type is an inner type. The default is to return false. Inner
* class generators must override.
*
* @return boolean
*/
public boolean isInner() {
return false;
}
/**
* Returns true if the generated type is an interface.
*
* @return boolean
*/
public boolean isInterface() {
return false;
}
/**
* Returns true if {@link JavaTypeGenerator#prepare()}has been called on this generator.
* Generally speaking, a type is prepared when one of the child member generators needs the type
* to be prepared. The framework handles this during run().
*/
public boolean isPrepared() throws GenerationException {
return fPrepared;
}
/**
* Populates the type descriptor. Subclasses generally do not need to override this unless doing
* so will save significant time.
*/
protected void populateTypeDescriptor(JavaTypeDescriptor desc) throws GenerationException {
super.populateMemberDescriptor(desc);
desc.setSuperInterfaceNames(getSuperInterfaceNames());
}
/**
* Generally speaking, a type is prepared when one of the child member generators needs the type
* to be prepared. The framework handles this during run().
*/
public void prepare() throws GenerationException {
if (!fPrepared) {
if (isInner()) {
if (!(getDeclaringTypeGenerator().isPrepared()))
getDeclaringTypeGenerator().prepare();
} else {
if (!isCompilationUnitPrepared())
prepareCompilationUnit();
}
setTargetElement(null);
fPrepared = true;
}
}
/**
* Prepares the compilation unit for a type. Makes sure the enclosing type's compilation unit is
* prepared for an inner type.
*
* @see JavaCompilationUnitGenerator#isPrepared()
*/
public void prepareCompilationUnit() throws GenerationException {
getCompilationUnitGenerator().prepare();
}
/**
* Populates the generation buffer with the class/interface specific part of the type
* declaration. The immediate subclasses in the framework implement this method.
*/
protected abstract void primTypeKindDecl(JavaTypeDescriptor desc, IGenerationBuffer typeBuf);
/**
* Reaccess the member from the working copy.
*/
IMember reaccess(IMember oldMember) throws GenerationException {
IMember result = null;
if (oldMember != null) {
if (isInner()) {
IType declType = getDeclaringType();
if (declType != null)
result = declType.getType(oldMember.getElementName());
} else {
ICompilationUnit cu = getCompilationUnit();
if (cu != null)
result = cu.getType(oldMember.getElementName());
}
if ((result != null) && (!result.exists()))
result = null;
}
return result;
}
/**
* Adds the post process step through the merge strategy for types.
*/
public void run() throws GenerationException {
super.run();
getCompilationUnitGenerator().getMergeStrategy().postProcess(this);
if (isNew())
formatContents();
}
protected void formatContents() throws GenerationException {
IType type = getType();
if (type != null && !type.isBinary()) {
try {
IOpenable openable = getType().getOpenable();
IBuffer buffer = openable.getBuffer();
if (buffer != null) {
String formatted = format(buffer.getContents());
int start, end;
start = formatted.indexOf(BEGIN_BRACE);
end = formatted.indexOf(END_BRACE);
if (end - start == 1) {
//We cannot have a situation where the class has no methods
//and ends with "{}" because it will not be able to gen any
//additional methods into it.
buffer.setContents(formatted.substring(0, start + 1));
buffer.append(IJavaGenConstants.LINE_SEPARATOR);
buffer.append(formatted.substring(end));
} else
buffer.setContents(formatted);
}
} catch (JavaModelException e) {
}
}
}
}