blob: c58d6fed00a1824f89dc5a2d2ee6c6a1578dd935 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 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:
* Luzius Meisser - initial implementation
*******************************************************************************/
package org.eclipse.ajdt.core.codeconversion;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.aspectj.asm.IProgramElement;
import org.aspectj.asm.IRelationship;
import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
import org.aspectj.org.eclipse.jdt.core.compiler.InvalidInputException;
import org.aspectj.org.eclipse.jdt.internal.compiler.parser.Scanner;
import org.aspectj.org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
import org.eclipse.ajdt.core.AspectJPlugin;
import org.eclipse.ajdt.core.model.AJProjectModelFacade;
import org.eclipse.ajdt.core.model.AJProjectModelFactory;
import org.eclipse.ajdt.core.model.AJRelationshipManager;
import org.eclipse.ajdt.core.model.AJRelationshipType;
import org.eclipse.ajdt.internal.core.ras.NoFFDC;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
/**
* The purpose of this parser is to convert AspectJ code into similar Java code
* which allows us to reuse for example for jdt formatting or comment
* generation.
*
* Depending on the ConversionOptions it gets called with, it does: - replace
* the keyword "aspect" by "class " - replace all the '.'s in intertype
* declarations by an '$'s to make them look like an ordinary declarations. -
* erase the keywords "returning", "throwing", "privileged", "issingleton" e.g.:
* "after() throwing (Exception e)" -> "after( Exception e)" - erase pointcut
* designators (includes "percflow" & co.) - add dummy references to all erased
* class references to end of buffer to make "organize imports" work correctly -
* add a reference to the target class inside intertype method declarations to
* simulate the context switch necessary to get proper code completion.
* (A detailed description of how code completion works in AJDT can be found in
* bug 74419.)
*
* Restrictions: - class names inside pointcut designators must begin with a
* capital letter to be recognised as such
*
*
* @author Luzius Meisser
*/
public class AspectsConvertingParser implements TerminalTokens, NoFFDC {
private static final String IMPLEMENTS = "implements";
private static final String EXTENDS = "extends";
private static final char[] throwing = "throwing".toCharArray(); //$NON-NLS-1$
private static final char[] returning = "returning".toCharArray(); //$NON-NLS-1$
private static final char[] percflow = "percflow".toCharArray(); //$NON-NLS-1$
private static final char[] percflowbelow = "percflowbelow".toCharArray(); //$NON-NLS-1$
private static final char[] perthis = "perthis".toCharArray(); //$NON-NLS-1$
private static final char[] pertarget = "pertarget".toCharArray(); //$NON-NLS-1$
private static final char[] issingleton = "issingleton".toCharArray(); //$NON-NLS-1$
private static final char[] pertypewithin = "pertypewithin".toCharArray(); //$NON-NLS-1$
private static final char[] classs = "class ".toCharArray(); //$NON-NLS-1$
private static final char[] privileged = " ".toCharArray(); //$NON-NLS-1$
private static final String thizString = "thiz"; //$NON-NLS-1$
private final static char[] tjpRefs2 = "org.aspectj.lang.JoinPoint thisJoinPoint; org.aspectj.lang.JoinPoint.StaticPart thisJoinPointStaticPart; org.aspectj.lang.JoinPoint.StaticPart thisEnclosingJoinPointStaticPart;" //$NON-NLS-1$
.toCharArray();
private final static char[] endThrow = new char[] { '(', ':' };
private final static Pattern perClausePattern = Pattern.compile("(percflow)|(percflowbelow)|(pertarget)|(pertypewithin)|(perthis)|(issingleton)");
public class Replacement {
//the position in the original char[]
public int posBefore;
//the position in the new char[], or -1 if not yet applied
public int posAfter;
//the number of chars that get replaced
public int length;
//the content to be inserted
public char[] text;
//the number of additional chars (lengthAdded == text.length - length)
public int lengthAdded;
public Replacement(int pos, int length, char[] text) {
this.posBefore = pos;
this.posAfter = -1;
this.length = length;
this.text = text;
lengthAdded = text.length - length;
}
}
public AspectsConvertingParser(char[] content) {
this.content = content;
this.typeReferences = new HashSet();
this.usedIdentifiers = new HashSet();
replacements = new ArrayList(5);
}
public char[] content;
private Set typeReferences;
private Set usedIdentifiers;
private ConversionOptions options;
/**
* can be null. used for determining ITDs
*/
private ICompilationUnit unit;
//list of replacements
//by convetion: sorted by posBefore in ascending order
private ArrayList replacements;
protected Scanner scanner;
private boolean inPointcutDesignator;
private boolean inAspect;
private boolean inAspectDeclaration;
private boolean inClassDeclaration;
private boolean inInterfaceDeclaration;
private int posColon;
public void setUnit(ICompilationUnit unit) {
this.unit = unit;
}
//returns a list of Insertions to let the client now what has been inserted
//into the buffer so he can translate positions from the old into the new
//buffer
public ArrayList convert(ConversionOptions options) {
this.options = options;
boolean insertIntertypeDeclarations = options
.isThisJoinPointReferencesEnabled();
boolean addReferencesForOrganizeImports = options
.isDummyTypeReferencesForOrganizeImportsEnabled();
boolean isSimulateContextSwitchNecessary = (options.getTargetType() != null);
scanner = new Scanner();
scanner.setSource(content);
inPointcutDesignator = false;
inAspect = false;
inAspectDeclaration = false;
inClassDeclaration = false;
inInterfaceDeclaration = false;
// Bug 93248: Count question marks so as to ignore colons that are part of conditional statements
int questionMarkCount = 0;
// count paren levels so that we know if we are in a intertype declaration name or not
int parenLevel = 0;
// Bug 110751: Ignore colons that are part of enhanced "for" loop in Java 5
boolean insideFor = false;
char[] currentTypeName = null;
replacements.clear();
typeReferences.clear();
usedIdentifiers.clear();
int tok;
int pos;
int typeDeclStart = 0;
while (true) {
try {
tok = scanner.getNextToken();
} catch (InvalidInputException e) {
continue;
}
if (tok == TokenNameEOF)
break;
switch (tok) {
case TokenNameIdentifier:
if (!inAspect && !inTypeDeclaration())
break;
char[] name = scanner.getCurrentIdentifierSource();
if (inTypeDeclaration() && !inPointcutDesignator) {
// only do this if we are not adding ITDs
if (inAspectDeclaration && !insertIntertypeDeclarations) {
if (CharOperation.equals(percflow, name)) {
startPointcutDesignator();
} else if (CharOperation.equals(percflowbelow, name)) {
startPointcutDesignator();
} else if (CharOperation.equals(perthis, name)) {
startPointcutDesignator();
} else if (CharOperation.equals(pertarget, name)) {
startPointcutDesignator();
} else if (CharOperation.equals(issingleton, name)) {
startPointcutDesignator();
} else if (CharOperation.equals(pertypewithin, name)) {
startPointcutDesignator();
}
}
// store the type name if not already found
if (currentTypeName == null) {
currentTypeName = name;
}
}
if (CharOperation.equals(throwing, name))
consumeRetOrThrow();
else if (CharOperation.equals(returning, name))
consumeRetOrThrow();
else if (inPointcutDesignator
&& Character.isUpperCase(name[0])
&& (content[scanner.getCurrentTokenStartPosition()-1]!='.')) {
typeReferences.add(new String(name));
}
if (isSimulateContextSwitchNecessary) {
usedIdentifiers.add(new String(name));
}
break;
case TokenNamefor:
insideFor=true;
break;
case TokenNameLPAREN:
parenLevel++;
break;
case TokenNameRPAREN:
insideFor=false;
parenLevel--;
break;
case TokenNameCOLON:
if (!inAspect)
break;
if (insideFor)
break;
if (questionMarkCount > 0) {
questionMarkCount--;
break;
}
startPointcutDesignator();
break;
case TokenNameQUESTION:
questionMarkCount++;
break;
case TokenNameSEMICOLON:
if (inPointcutDesignator)
endPointcutDesignator();
break;
case TokenNameDOT:
if (!inAspect) {
break;
} else if (inPointcutDesignator) {
break;
} else if (parenLevel > 0) {
// don't want to convert '.' to '$' inside the parameter declaration
break;
}
processPotentialIntertypeDeclaration();
break;
case TokenNameLBRACE:
if (inPointcutDesignator) {
endPointcutDesignator();
//must be start of advice body -> insert tjp reference
if (insertIntertypeDeclarations && !inTypeDeclaration()) {
addReplacement(scanner.getCurrentTokenStartPosition() + 1, 0,
tjpRefs2);
}
}
// determine if we should add intertype declarations
if (insertIntertypeDeclarations && inTypeDeclaration()) {
char[] implementsExtends = createImplementExtendsITDs(currentTypeName);
if (implementsExtends != null) {
addReplacement(typeDeclStart, scanner.getCurrentTokenStartPosition()-typeDeclStart, implementsExtends);
} else {
// not able to add ITDs since no compilation unit available
// may have to replace "aspect" with "class " since we didn't do that before
if (hasWordAtPosition("aspect", typeDeclStart)) {
addReplacement(typeDeclStart, classs.length, classs);
}
}
char[] interTypeDecls = getInterTypeDecls(currentTypeName);
if (interTypeDecls.length > 0) {
addReplacement(scanner.getCurrentTokenStartPosition() + 1, 0, interTypeDecls);
}
}
inAspectDeclaration = false;
inClassDeclaration = false;
inInterfaceDeclaration = false;
currentTypeName = null;
// insideBlockCount ++;
break;
case TokenNameRBRACE:
// insideBlockCount ++;
if (inPointcutDesignator) {
// bug 129367: if we've hit a } here, we must be
// in the middle of an unterminated pointcut
endPointcutDesignator();
}
break;
case TokenNameaspect:
inAspect = true;
inAspectDeclaration = true;
typeDeclStart = pos = scanner.getCurrentTokenStartPosition();
// only do this if not inserting ITDs
// if inserting ITDs, this replacement occurs in createImplementExtendsITDs()
if (!insertIntertypeDeclarations) {
addReplacement(pos, classs.length, classs);
}
break;
case TokenNameclass:
typeDeclStart = pos = scanner.getCurrentTokenStartPosition();
inClassDeclaration = true;
break;
case TokenNameinterface: // interface and @interface
typeDeclStart = pos = scanner.getCurrentTokenStartPosition();
inInterfaceDeclaration = true;
break;
case TokenNameprivileged:
pos = scanner.getCurrentTokenStartPosition();
addReplacement(pos, privileged.length, privileged);
break;
}
}
if (inPointcutDesignator) {
// bug 129367: if we've hit the end of the buffer, we must
// be in the middle of an unterminated pointcut
endPointcutDesignator();
}
if (addReferencesForOrganizeImports)
addReferences();
if (isSimulateContextSwitchNecessary)
simulateContextSwitch(options.getCodeCompletePosition(), options
.getTargetType());
applyReplacements();
//System.out.println(new String(content));
return replacements;
}
/**
*
* @param typeName name of the type
* @return new type declaration string to replace the original
* that contains all of the types that are declared parents of this one
* returns null if could not find the super types and super interfaces
*/
protected char[] createImplementExtendsITDs(char[] typeName) {
if (unit != null) {
IType type = unit.getType(new String(typeName));
if (type.exists()) {
try {
StringBuffer sb = new StringBuffer();
sb.append(type.isInterface() ? "interface " : "class ");
sb.append(typeName);
List[] declares = getDeclareExtendsImplements(type);
List declareExtends = declares[0];
List declareImplements = declares[1];
if (! type.isInterface()) {
String superClass = type.getSuperclassName();
if (declareExtends.size() > 0) {
superClass = (String) declareExtends.get(0);
superClass = superClass.replace('$', '.');
}
if (superClass != null) {
sb.append(" " + EXTENDS + " " + superClass);
}
}
String[] superInterfaces = type.getSuperInterfaceNames();
for (int i = 0; i < superInterfaces.length; i++) {
declareImplements.add(superInterfaces[i]);
}
if (declareImplements.size() > 0) {
if (type.isInterface()) {
sb.append(" " + EXTENDS + " ");
} else {
sb.append(" " + IMPLEMENTS + " ");
}
for (Iterator interfaceIter = declareImplements.iterator(); interfaceIter
.hasNext();) {
String interName = (String) interfaceIter.next();
interName = interName.replace('$', '.');
sb.append(" " + interName);
if (interfaceIter.hasNext()) {
sb.append(",");
}
}
}
return sb.toString().toCharArray();
} catch (JavaModelException e) {
AspectJPlugin.getDefault().getLog().log(new Status(Status.ERROR, AspectJPlugin.PLUGIN_ID, e.getMessage(), e));
return null;
}
}
}
return null;
}
/**
* @param type type to look for declare extends on
* @return list of all declare extends that apply to this type
* in fully qualified strings
*/
protected List/*String*/[] getDeclareExtendsImplements(IType type) {
List declareExtends = new ArrayList();
List declareImplements = new ArrayList();
if (type != null && type.exists()) {
AJProjectModelFacade model = AJProjectModelFactory.getInstance().getModelForJavaElement(type);
if (model.hasModel()) {
List /*IJavaElement*/ rels = model.getRelationshipsForElement(type, AJRelationshipManager.ASPECT_DECLARATIONS);
for (Iterator eltIter = rels.iterator(); eltIter.hasNext();) {
IJavaElement je = (IJavaElement) eltIter.next();
IProgramElement pe = model.javaElementToProgramElement(je);
if (pe.getKind() == IProgramElement.Kind.DECLARE_PARENTS) {
List/*String*/ parentTypes = pe.getParentTypes();
String details = pe.getDetails();
if (details.startsWith(EXTENDS)) {
declareExtends.addAll(parentTypes);
} else if (details.startsWith(IMPLEMENTS)) {
declareImplements.add(parentTypes);
}
}
}
}
}
return new List[] { declareExtends, declareImplements };
}
protected char[] getInterTypeDecls(char[] currentTypeName) {
if (unit != null) {
AJProjectModelFacade model = AJProjectModelFactory.getInstance().getModelForJavaElement(unit);
if (model.hasModel()) {
IType type = unit.getType(new String(currentTypeName));
if (type.exists()) {
List /*IJavaElement*/ rels = model.getRelationshipsForElement(type, AJRelationshipManager.ASPECT_DECLARATIONS);
StringBuffer sb = new StringBuffer("\n\t");
for (Iterator relIter = rels.iterator(); relIter.hasNext();) {
IJavaElement je = (IJavaElement) relIter.next();
IProgramElement declareElt = model.javaElementToProgramElement(je);
if (declareElt != null && declareElt.getParent() != null && declareElt.getKind().isInterTypeMember()) { // checks to see if this element is valid
// should be fully qualified type and simple name
int lastDot = declareElt.getName().lastIndexOf('.');
String name = declareElt.getName().substring(lastDot+1);
if (declareElt.getKind() == IProgramElement.Kind.INTER_TYPE_FIELD) {
sb.append(declareElt.getCorrespondingType(true) + " " + name + ";\n");
} else if (declareElt.getKind() == IProgramElement.Kind.INTER_TYPE_METHOD ||
declareElt.getKind() == IProgramElement.Kind.INTER_TYPE_CONSTRUCTOR) {
// need to add a return statement?
if (declareElt.getKind() == IProgramElement.Kind.INTER_TYPE_METHOD) {
sb.append(declareElt.getCorrespondingType(true) + " " + name);
} else {
sb.append(currentTypeName);
}
sb.append("(");
List/*String*/ names = declareElt.getParameterNames();
List/*String*/ types = declareElt.getParameterTypes();
for (Iterator typeIter = types.iterator(), nameIter = names.iterator();
typeIter.hasNext();) {
String paramType = new String((char[]) typeIter.next());
String paramName = (String) nameIter.next();
sb.append(paramType + " " + paramName);
if (typeIter.hasNext()) {
sb.append(", ");
}
}
sb.append(") { }\n");
}
}
}
return sb.toString().toCharArray();
}
}
}
return new char[0];
}
private boolean inTypeDeclaration() {
return (inAspectDeclaration ||
inClassDeclaration ||
inInterfaceDeclaration);
}
/**
* Inserts a reference to targetType at the given position. Thanks to this,
* we can simulate the context switch necessary in intertype method
* declarations.
*
* Transformations: - Insertion of local variable 'TargetType thiz' (or, if
* thiz is already used, a number is added to thiz to make it unique) at
* start of method mody
* - if code completion on code like 'this.methodcall().more...', 'this
* gets replaced by thiz
* - if code completion on code like 'methodcall().more...', 'thiz.' gets
* added in front.
*
* How the correct place for insertion is found: -
*
*
*/
private void simulateContextSwitch(int position, char[] targetType) {
int pos = findInsertionPosition(position - 1) + 1;
//if code completion on 'this' -> overwrite the this keyword
int len = 0;
if ((content[pos] == 't') && (content[pos + 1] == 'h')
&& (content[pos + 2] == 'i') && (content[pos + 3] == 's')
&& !Character.isJavaIdentifierPart(content[pos + 4]))
len = 4;
String ident = findFreeIdentifier();
char[] toInsert = (new String(targetType) + ' ' + ident + ';' + ident + '.')
.toCharArray();
addReplacement(pos, len, toInsert);
}
/**
* @return An unused identifier
*/
private String findFreeIdentifier() {
int i = 0;
String ident = thizString + i;
while (usedIdentifiers.contains(ident)) {
i++;
ident = thizString + i;
}
return ident;
}
/**
* @param pos -
* a code position
* @return the position that defines the context of the current one at the
* highest level
*
* e.g. ' this.doSomthing().get' with pos on the last 't' returns the
* position of the char before the first 't'
*/
private int findInsertionPosition(int pos) {
char ch = content[pos];
int currentPos = pos;
if (Character.isWhitespace(ch)) {
currentPos = findPreviousNonSpace(pos);
if (currentPos == -1)
return pos;
ch = content[currentPos];
if (ch == '.')
return findInsertionPosition(--currentPos);
return pos;
}
if (Character.isJavaIdentifierPart(ch)) {
while (Character.isJavaIdentifierPart(ch)) {
currentPos--;
ch = content[currentPos];
}
return findInsertionPosition(currentPos);
}
if (ch == '.') {
return findInsertionPosition(--pos);
}
if (ch == ')') {
currentPos--;
int bracketCounter = 1;
while (currentPos >= 0) {
ch = content[currentPos];
if (bracketCounter == 0)
break;
if (ch == ')')
bracketCounter++;
if (ch == '(') {
bracketCounter--;
if (bracketCounter < 0)
return -1;
}
currentPos--;
}
return findInsertionPosition(currentPos);
}
return pos;
}
private void applyReplacements() {
Iterator iter = replacements.listIterator();
int offset = 0;
while (iter.hasNext()) {
Replacement ins = (Replacement) iter.next();
ins.posAfter = ins.posBefore + offset;
replace(ins.posAfter, ins.length, ins.text);
offset += ins.lengthAdded;
}
}
private void replace(int pos, int origLength, char[] text) {
if (origLength != text.length) {
int lengthDiff = text.length - origLength;
int oldEnd = pos + origLength;
int newEnd = pos + text.length;
char[] temp = new char[content.length + lengthDiff];
System.arraycopy(content, 0, temp, 0, pos);
System.arraycopy(content, oldEnd, temp, newEnd, content.length
- oldEnd);
content = temp;
}
System.arraycopy(text, 0, content, pos, text.length);
}
private void startPointcutDesignator() {
if (inPointcutDesignator)
return;
inPointcutDesignator = true;
posColon = scanner.getCurrentTokenStartPosition();
}
/**
*
*/
private void endPointcutDesignator() {
inPointcutDesignator = false;
int posSemi = scanner.getCurrentTokenStartPosition();
int len = posSemi - posColon;
char[] empty = new char[len];
for (int i = 0; i < empty.length; i++) {
empty[i] = ' ';
}
addReplacement(posColon, len, empty);
}
//identifies intertype declaration of form 'type qualifier.membername'
//and replaces every '.' by '$'.
//e.g. "int tracing.Circle.x;" -> "int tracing$Circle$x;"
private void processPotentialIntertypeDeclaration() {
//pos points to the '.'
int pos = scanner.getCurrentTokenStartPosition();
//check if valid identifier char on left side of dot
//(to sort out construct like '(new Object()).' )
int nonspace1 = findPreviousNonSpace(pos - 1);
if (nonspace1 == -1)
return;
if (!Character.isJavaIdentifierPart(content[nonspace1]))
return;
//check if there is another java identifier before qualifier,
//if no, return (to sort out method calls and the like)
int space = findPreviousSpace(nonspace1);
if (space == -1) {
return;
}
int nonspace2 = findPreviousNonSpace(space);
if (nonspace2 == -1) {
return;
}
if (!Character.isJavaIdentifierPart(content[nonspace2]) &&
content[nonspace2] != '>') { // could be a parameterized type
// but still could be part of a "new" ITD
int nextNonSpace = findNextNonSpace(pos+1);
if (!hasWordAtPosition("new", nextNonSpace)) {
// XXX problem here is that this doesn't account for comments
return;
}
}
//check if rightmost part of qualifier starts with Capital letter,
//and if yes, assume it is a Class name -> intertype declaration
int spaceordot = findPreviousWhitespaceOr(',', nonspace1);
if (spaceordot == -1)
return;
if (Character.isUpperCase(content[spaceordot + 1])) {
//assume intertype declaration and replace all '.' by '$'
char[] rep = new char[] { '$' };
addReplacement(pos, 1, rep);
if (content[spaceordot] == ' ') {
String type = new String(content, space + 1, pos - space - 1);
boolean validIdentifier = true;
for (int i = 0; validIdentifier && (i < type.length()); i++) {
char c = type.charAt(i);
if (i==0) {
if (!Character.isJavaIdentifierStart(c)) {
validIdentifier = false;
}
} else if (!Character.isJavaIdentifierPart(c)) {
validIdentifier = false;
}
}
if (validIdentifier) {
typeReferences.add(type);
}
} else {
do {
addReplacement(spaceordot, 1, rep);
spaceordot = findPreviousWhitespaceOr(',', --spaceordot);
} while (content[spaceordot] == '.');
}
//if requested, add ajc$ in front of intertype declaration
//e.g. "public int Circle$x;" -> "public int ajc$Circle$x;"
if (options.isAddAjcTagToIntertypesEnabled()) {
addReplacement(spaceordot + 1, 0, "ajc$".toCharArray()); //$NON-NLS-1$
}
}
}
private boolean hasWordAtPosition(String string, int pos) {
char[] word = string.toCharArray();
for (int i = 0; i < word.length; i++) {
if (word[i] != content[pos+i] ) {
return false;
}
}
return true;
}
public int findPrevious(char ch, int pos) {
while (pos >= 0) {
if (content[pos] == ch)
return pos;
pos--;
}
return -1;
}
public int findPreviousWhitespaceOr(char ch, int pos) {
while (pos >= 0) {
if (content[pos] == ch || Character.isWhitespace(content[pos])) {
return pos;
}
pos--;
}
return -1;
}
public int findPrevious(char[] chs, int pos) {
while (pos >= 0) {
for (int i = 0; i < chs.length; i++) {
if (content[pos] == chs[i])
return pos;
}
pos--;
}
return -1;
}
public int findPreviousSpace(int pos) {
while (pos >= 0) {
if (Character.isWhitespace(content[pos]))
return pos;
pos--;
}
return -1;
}
public int findPreviousNonSpace(int pos) {
while (pos >= 0) {
if (!Character.isWhitespace(content[pos]))
return pos;
pos--;
}
return -1;
}
public int findNextNonSpace(int pos) {
while (pos < content.length) {
if (!Character.isWhitespace(content[pos]))
return pos;
pos++;
}
return -1;
}
public int findNext(char[] chs, int pos) {
while (pos < content.length) {
for (int i = 0; i < chs.length; i++) {
if (content[pos] == chs[i])
return pos;
}
pos++;
}
return -1;
}
private void consumeRetOrThrow() {
int pos = scanner.getCurrentTokenStartPosition();
char[] content = scanner.source;
int end = findNext(endThrow, pos);
if (end == -1)
return;
char[] temp = null;
if (content[end] == endThrow[0]) {
pos = findPrevious(')', pos);
if (pos == -1)
return;
int advicebracket = findPrevious('(', pos);
if (advicebracket == -1)
return;
temp = new char[end - pos + 1];
if (bracketsContainSomething(advicebracket)
&& bracketsContainSomething(end))
temp[0] = ',';
else
temp[0] = ' ';
for (int i = 1; i < temp.length; i++) {
temp[i] = ' ';
}
} else {
temp = new char[end - pos];
for (int i = 0; i < temp.length; i++) {
temp[i] = ' ';
}
}
addReplacement(pos, temp.length, temp);
}
/**
* @param end
* @return
*/
private boolean bracketsContainSomething(int start) {
while (++start < content.length) {
if (content[start] == ')')
return false;
if (Character.isJavaIdentifierPart(content[start]))
return true;
}
return false;
}
private int findLast(char ch) {
int pos = content.length;
while (--pos >= 0) {
if (content[pos] == ch)
break;
}
return pos;
}
//adds references to all used type -> organize imports will work
private void addReferences() {
if (typeReferences == null)
return;
//char[] decl = new char[] { ' ', 'x', ';' };
int pos = findLast('}');
if (pos < 0)
return;
StringBuffer temp = new StringBuffer(typeReferences.size() * 10);
Iterator iter = typeReferences.iterator();
int varCount=1;
while (iter.hasNext()) {
String ref = (String) iter.next();
temp.append(ref);
temp.append(" x"); //$NON-NLS-1$
temp.append(varCount++);
temp.append(';');
}
char[] decls = new char[temp.length()];
temp.getChars(0, decls.length, decls, 0);
addReplacement(pos, 0, decls);
}
//adds a replacement to list
//pre: list sorted, post: list sorted
private void addReplacement(int pos, int length, char[] text) {
int last = replacements.size() - 1;
while (last >= 0) {
if (((Replacement) replacements.get(last)).posBefore < pos)
break;
last--;
}
replacements.add(last + 1, new Replacement(pos, length, text));
}
public static boolean conflictsWithAJEdit(int offset, int length,
ArrayList replacements) {
Replacement ins;
for (int i = 0; i < replacements.size(); i++) {
ins = (Replacement) replacements.get(i);
if ((offset >= ins.posAfter) && (offset < ins.posAfter + ins.length)) {
return true;
}
if ((offset < ins.posAfter) && (offset + length > ins.posAfter)) {
return true;
}
}
return false;
}
//translates a position from after to before changes
//if the char at that position did not exist before, it returns the
// position before the inserted area
public static int translatePositionToBeforeChanges(int posAfter,
ArrayList replacements) {
Replacement ins;
int offset = 0, i;
for (i = 0; i < replacements.size(); i++) {
ins = (Replacement) replacements.get(i);
if (ins.posAfter > posAfter)
break;
offset += ins.lengthAdded;
}
if (i > 0) {
ins = (Replacement) replacements.get(i - 1);
if (ins.posAfter + ins.text.length > posAfter) {
//diff must be > 0
int diff = posAfter - ins.posAfter;
if (diff > ins.length)
//we are in inserted area -> return pos directly before
// that area
offset += diff - ins.length;
}
}
return posAfter - offset;
}
//translates a position from before to after changes
public static int translatePositionToAfterChanges(int posBefore,
ArrayList replacements) {
for (int i = 0; i < replacements.size(); i++) {
Replacement ins = (AspectsConvertingParser.Replacement) replacements
.get(i);
if (ins.posAfter <= posBefore)
posBefore += ins.lengthAdded;
else
return posBefore;
}
return posBefore;
}
}