| /******************************************************************************* |
| * 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.IMember; |
| import org.eclipse.jdt.core.IMethod; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.Signature; |
| import org.eclipse.jdt.core.jdom.IDOMMember; |
| import org.eclipse.jdt.core.jdom.IDOMMethod; |
| |
| |
| /** |
| * The default method merglet. |
| */ |
| public class JavaMethodMerglet extends JavaMemberMerglet { |
| private static final String METHOD_COLLISION_INFORMATIONAL = JavaCodeGenResourceHandler.getString("The_method_will_not_gen_INFO_"); //$NON-NLS-1$ = "The method \"{1}\" will not be generated because a non-generated method with the same signature already exists." |
| private static final String METHOD_NOT_DELETED_INFORMATIONAL = JavaCodeGenResourceHandler.getString("The_method_will_not_del_INFO_"); //$NON-NLS-1$ = "The method \"{0}\" will not be deleted because it is not marked generated." |
| private static final String ABSTRACT_METHOD_BODY = ";"; //$NON-NLS-1$ |
| private String[] fNoMergeExceptionNames = null; |
| |
| /** |
| * JavaMethodMerglet default constructor. |
| */ |
| public JavaMethodMerglet(IJavaMergeStrategy jms) { |
| super(jms); |
| } |
| |
| /** |
| * Returns the body of the method. The method body includes all code following the method |
| * declaration, including the enclosing braces. For abstract or native methods a string |
| * containing just a semicolon is returned. That is the value returned by JDOM in those cases. |
| * |
| * @return java.lang.String |
| * @param theMethod |
| * org.eclipse.jdt.core.IMethod |
| */ |
| protected String getBody(IMethod theMethod) throws MergeException { |
| try { |
| // If the method is abstract or native. |
| // The semicolon is the body. This is what JDOM returns |
| // in this case. |
| String body = ABSTRACT_METHOD_BODY; |
| String source = theMethod.getSource(); |
| int sourceIndex = theMethod.getNameRange().getOffset() - theMethod.getSourceRange().getOffset(); |
| sourceIndex = source.indexOf('{', sourceIndex); |
| // If it has a body, return the body |
| if (sourceIndex >= 0) { |
| sourceIndex--; |
| while ((sourceIndex >= 0) && (Character.isWhitespace(source.charAt(sourceIndex)))) |
| sourceIndex--; |
| sourceIndex++; |
| body = source.substring(sourceIndex); |
| } |
| return body; |
| } catch (JavaModelException exc) { |
| throw new MergeException(exc); |
| } |
| } |
| |
| /** |
| * Returns the typical reason why a method is not generated during a collision. |
| */ |
| protected Object getMemberCollisionReason() { |
| return METHOD_COLLISION_INFORMATIONAL; |
| } |
| |
| /** |
| * Returns the typical reason why an old method is not deleted during a generation. |
| */ |
| protected Object getMemberNotDeletedReason() { |
| return METHOD_NOT_DELETED_INFORMATIONAL; |
| } |
| |
| /** |
| * The method generator or merge strategy can specify exceptions that should not be merged from |
| * the old method to the new method via this property. |
| * |
| * @return java.lang.String[] |
| */ |
| public java.lang.String[] getNoMergeExceptionNames() { |
| return fNoMergeExceptionNames; |
| } |
| |
| /** |
| * Returns the readable identification of the member. |
| */ |
| protected String getReadableIdFor(IMember member) throws MergeException { |
| try { |
| return Signature.toString(((IMethod) member).getSignature(), member.getElementName(), null, false, true); |
| } catch (JavaModelException exc) { |
| throw new MergeException(exc); |
| } |
| } |
| |
| /** |
| * Returns true if old user code is found and merged. |
| * <p> |
| * Note that the support of VAJ style user code points is here mainly for compatibility and |
| * migration purposes. For new development, the use of specialized merglets is encouraged |
| * instead. |
| * |
| * @exception java.lang.RuntimeException |
| * if a user code point format error is found. |
| */ |
| protected boolean injectUserCode(String name, String oldSource, StringBuffer mergedSource) throws RuntimeException { |
| org.eclipse.jst.j2ee.internal.codegen.GenerationBuffer genBuf = new org.eclipse.jst.j2ee.internal.codegen.GenerationBuffer(); |
| genBuf.format(getJavaMergeStrategy().getUserCodeBeginTemplate(), new String[]{name}); |
| String codePointTag = genBuf.toString(); |
| int oldUserCodePos = oldSource.indexOf(codePointTag); |
| boolean merged = false; |
| if (oldUserCodePos >= 0) { |
| oldUserCodePos += codePointTag.length(); |
| int endOldUserCodePos = oldSource.indexOf(getJavaMergeStrategy().getUserCodeEnd(), oldUserCodePos); |
| if (endOldUserCodePos < oldUserCodePos) |
| throw new RuntimeException(); |
| String oldUserCode = oldSource.substring(oldUserCodePos, endOldUserCodePos); |
| // If it is all whitespace, skip it. |
| for (int i = 0; (!merged && (i < oldUserCode.length())); i++) |
| merged = !(Character.isWhitespace(oldUserCode.charAt(i))); |
| if (merged) |
| mergedSource.append(oldUserCode); |
| } |
| return merged; |
| } |
| |
| /** |
| * Returns true if the two method bodies are equivalent. This implementation just string |
| * compares the bodies after trimming whitespace. The method body includes all code following |
| * the method declaration, including the enclosing braces. |
| * |
| * @exception org.eclipse.jst.j2ee.internal.internal.internal.java.codegen.MergeException |
| */ |
| protected boolean matchBody(IMethod collisionMethod, IDOMMethod newMethod) throws MergeException { |
| return getBody(collisionMethod).trim().equals(newMethod.getBody().trim()); |
| } |
| |
| /** |
| * Merge the body of the old method and the new method together. Returns false if the new method |
| * code is taken as is. Returns true if merged or the old method code was taken as is. This |
| * implementation just merges user code points. |
| * <p> |
| * Note that the support of VAJ style user code points is here mainly for compatibility and |
| * migration purposes. For new development, the use of specialized merglets is encouraged |
| * instead. |
| * |
| * @return boolean |
| * @param oldMethod |
| * org.eclipse.jdt.core.IMethod |
| * @param newMethod |
| * org.eclipse.jdt.core.jdom.IDOMMethod |
| */ |
| protected boolean mergeBody(IMethod oldMethod, IDOMMethod newMethod) throws MergeException { |
| return mergeUserCode(oldMethod, newMethod); |
| } |
| |
| /** |
| * If the old method has exceptions that the new one does not have, merge in the missing ones. |
| * Returns true if there were exceptions to merge. |
| * |
| * @return boolean |
| * @param oldMethod |
| * org.eclipse.jdt.core.IMethod |
| * @param newMethod |
| * org.eclipse.jdt.core.jdom.IDOMMethod |
| */ |
| protected boolean mergeExceptions(IMethod oldMethod, IDOMMethod newMethod) throws MergeException { |
| boolean merged = false; |
| try { |
| String[] oldExceptionNames = makeReadable(oldMethod.getExceptionTypes()); |
| if (!((oldExceptionNames == null) || (oldExceptionNames.length == 0))) { |
| String[] newExceptionNames = newMethod.getExceptions(); |
| IType oldType = oldMethod.getDeclaringType(); |
| String[] noMergeExceptionNames = getNoMergeExceptionNames(); |
| for (int i = 0; i < oldExceptionNames.length; i++) { |
| int found = findTypeNameMatch(oldType, oldExceptionNames[i], newExceptionNames); |
| if (found < 0) { |
| found = findTypeNameMatch(oldType, oldExceptionNames[i], noMergeExceptionNames); |
| if (found < 0) { |
| newMethod.addException(oldExceptionNames[i]); |
| merged = true; |
| } |
| } |
| } |
| } |
| } catch (JavaModelException exc) { |
| throw new MergeException(exc); |
| } |
| return merged; |
| } |
| |
| /** |
| * Merges the member. This method is called only if the old member of the merge results is not |
| * null. If the old member is null, there is nothing to merge from. Returns true if merging was |
| * done. Uses {@link JavaMethodMerglet#mergeExceptions(IMethod, IDOMMethod)}, |
| * {@link JavaMethodMerglet#mergeBody(IMethod, IDOMMethod)}and |
| * {@link JavaMethodMerglet#mergeReturnTypes(IMethod, IDOMMethod)}. |
| * |
| * @return boolean |
| * @param oldMember |
| * org.eclipse.jdt.core.IMember |
| * @param newMember |
| * org.eclipse.jdt.core.jdom.IDOMMember |
| */ |
| protected boolean mergeMember(IMember oldMember, IDOMMember newMember) throws MergeException { |
| boolean merged = mergeExceptions((IMethod) oldMember, (IDOMMethod) newMember); |
| merged |= mergeBody((IMethod) oldMember, (IDOMMethod) newMember); |
| merged |= mergeReturnTypes((IMethod) oldMember, (IDOMMethod) newMember); |
| return merged; |
| } |
| |
| /** |
| * Returns true if the oldMethod return type is different from the newMethod return type. |
| * |
| * @return boolean |
| * @param oldMethod |
| * org.eclipse.jdt.core.IMethod |
| * @param newMethod |
| * org.eclipse.jdt.core.jdom.IDOMMethod |
| */ |
| protected boolean mergeReturnTypes(IMethod oldMethod, IDOMMethod newMethod) throws MergeException { |
| try { |
| String oldName, newName; |
| oldName = makeReadable(oldMethod.getReturnType()); |
| newName = newMethod.getReturnType(); |
| return !matchTypeNames(oldMethod.getDeclaringType(), oldName, newName); |
| } catch (JavaModelException exc) { |
| throw new MergeException(exc); |
| } |
| } |
| |
| /** |
| * Merges user code from the old method into the new. Returns true if merging happens. |
| * <p> |
| * Note that the support of VAJ style user code points is here mainly for compatibility and |
| * migration purposes. For new development, the use of specialized merglets is encouraged |
| * instead. |
| * |
| * @exception org.eclipse.jst.j2ee.internal.internal.internal.java.codegen.MergeException |
| */ |
| protected boolean mergeUserCode(IMethod oldMethod, IDOMMethod newMethod) throws MergeException { |
| boolean merged = false; |
| try { |
| String oldSource = getBody(oldMethod); |
| String newSource = newMethod.getBody(); |
| StringBuffer mergedSource = new StringBuffer(newSource.length() * 2); |
| |
| // Get the position of the first user code point in the new source |
| // and start looping to merge them all. |
| int namePos = 0; |
| int nameEnd = 0; |
| int doneSourcePos = 0; |
| String name = null; |
| boolean mergedThisPoint = false; |
| String userCodeBegin = getJavaMergeStrategy().getUserCodeBegin(); |
| int userCodePos = newSource.indexOf(userCodeBegin); |
| while (userCodePos >= 0) { |
| // Extract the name of the user code point. |
| namePos = newSource.indexOf('{', userCodePos) + 1; |
| if (namePos < userCodePos) |
| throw new RuntimeException(); |
| nameEnd = newSource.indexOf('}', namePos); |
| if (nameEnd < namePos) |
| throw new RuntimeException(); |
| name = newSource.substring(namePos, nameEnd); |
| |
| // Stash what we have so far. |
| userCodePos = nameEnd + 1; |
| mergedSource.append(newSource.substring(doneSourcePos, userCodePos)); |
| doneSourcePos = userCodePos; |
| |
| // Merge user code from the old code into the new code at this point. |
| mergedThisPoint = injectUserCode(name, oldSource, mergedSource); |
| if (mergedThisPoint) { |
| doneSourcePos = newSource.indexOf(getJavaMergeStrategy().getUserCodeEnd(), doneSourcePos); |
| if (doneSourcePos == -1) |
| throw new RuntimeException(); |
| merged = true; |
| } |
| |
| // Get the next user code point in the new source. |
| userCodePos = newSource.indexOf(userCodeBegin, doneSourcePos); |
| } |
| |
| // If we merged, get the last bit of the new source and update the results. |
| if (merged) { |
| mergedSource.append(newSource.substring(doneSourcePos)); |
| newMethod.setBody(mergedSource.toString()); |
| } |
| } catch (RuntimeException rtExc) { |
| // We get here because of a user code point format error. |
| // Do not merge and eat the error. |
| merged = false; |
| } |
| return merged; |
| } |
| |
| /** |
| * Check the collision method and the new method. 1. same flags 2. same super interfaces - uses |
| * {@link JavaMerglet#matchTypeNames(IType, String[], String[])}3. same body - uses |
| * {@link JavaMethodMerglet#matchBody(IMethod, IDOMMethod)}Do not have to check the name or |
| * types since that defines collision. Returns true if any of the above are not the same. |
| * |
| * @return boolean |
| * @param collisionMember |
| * org.eclipse.jdt.core.IMember |
| * @param newMember |
| * org.eclipse.jdt.core.jdom.IDOMMember |
| */ |
| protected boolean needToGenerate(IMember collisionMember, IDOMMember newMember) throws MergeException { |
| boolean needToGenerate = false; |
| IMethod collisionMethod = (IMethod) collisionMember; |
| IDOMMethod newMethod = (IDOMMethod) newMember; |
| |
| try { |
| // First, check the flags. |
| needToGenerate = !(collisionMethod.getFlags() == newMember.getFlags()); |
| |
| //Second, check the return types. |
| IType collisionType = null; |
| if (!needToGenerate) { |
| collisionType = collisionMethod.getDeclaringType(); |
| needToGenerate = !matchTypeNames(collisionType, makeReadable(collisionMethod.getReturnType()), newMethod.getReturnType()); |
| } |
| |
| // Third, check the exceptions. |
| if (!needToGenerate) { |
| needToGenerate = !matchTypeNames(collisionType, makeReadable(collisionMethod.getExceptionTypes()), newMethod.getExceptions()); |
| } |
| |
| // Fourth, check the body. |
| if (!needToGenerate) |
| needToGenerate = !matchBody(collisionMethod, newMethod); |
| } catch (JavaModelException exc) { |
| throw new MergeException(exc); |
| } |
| |
| return needToGenerate; |
| } |
| |
| /** |
| * The method generator or merge strategy can specify exceptions that should not be merged from |
| * the old method to the new method via this property. |
| * |
| * @param newNoMergeExceptionNames |
| * java.lang.String[] |
| */ |
| public void setNoMergeExceptionNames(java.lang.String[] newNoMergeExceptionNames) { |
| fNoMergeExceptionNames = newNoMergeExceptionNames; |
| } |
| } |