A bedolgozó a JDT API használatával létrehozhat osztályokat vagy felületeket, létező típusokhoz adhat metódusokat, vagy típusokhoz igazíthat metódusokat.
A Java objektumok megváltoztatásának legegyszerűbb módja a Java elem API használata. Általánosabb eljárások használhatók Java elemek nyers forráskódjának kezelésére.
Fordítási egység előállítása programozási eljárással legegyszerűbben az IPackageFragment.createCompilationUnit meghívásával lehetséges. Meg kell adni a fordítási egység nevét és tartalmát. A csomagban létrejön a fordítási egység és az új ICompilationUnit visszatér.
Fordítási egységet általánosan úgy lehet létrehozni, ha létrehoz egy fájl forrást ".java" kiterjesztéssel a csomag könyvtárával megegyező, megfelelő mappában. Az általános erőforrás API használata egy kerülő út a Java eszközökhöz, így a Java modell nem frissül addig, míg az általános erőforrás változás figyelők nem kapnak értesítést és a JDT figelők nem frissítik a Java modellt az új fordítási egységgel.
A Java forrás legegyszerűbb módosításait a Java elem API felülettel végezhetjük el.
Például lekérdezheti a fordítási egység egy típusát. Ha már az IType ismert, akkor forráskód tagokat adhat a típusoz olyan protokollokkal, mint a createField, a createInitializer, vagy a createMethod, vagy a createType. A forráskódot és a tag helyének információit ezekben a metódusokban kell megadni.
Az ISourceManipulation felület a Java elemek általános forráskezelését határozza meg. A felület a típus tagok átnevezéséhez, áthelyezéséhez, másolásához vagy törléséhez biztosít metódusokat.
Kódmódosítás a fordítási egység kezelésével végezhető (és így az alapul szolgáló IFile is módosul) vagy a másik megoldás ha a fordítási egység memóriában lévő másolatát módosíthatja; ez utóbbi neve a munkamásolat.
A fordítási egységből nyerhető a munkamásolat a getWorkingCopy metódus használatával. (Nem szükséges, hogy a munkamásolat létrehozásához a fordítási egység létezzen a Java modellben.) Bárki aki létrehoz egy munkamásolatot felelős a megszüntetéséért is, ha arra már nincs szükség. A megszüntetéshez a discardWorkingCopy metódus használható.
A munkamásolatok egy memóriában lévő puffert módosítanak. A getWorkingCopy() metódus létrehoz egy alapértelmezett puffert, de az ügyfél a saját puffer megvalósítását is fenntarthatja a getWorkingCopy(WorkingCopyOwner, IProblemRequestor, IProgressMonitor) metódus használatával. Ennek a puffernek a szövegét az ügyfél közvetlenül kezelheti. Ha így tesz, akkor időről-időre a munkamásolatot szinkronizálni kell a pufferrel a reconcile(int, boolean, WorkingCopyOwner, IProgressMonitor) metódus használatával.
Végül a munkamásolat elmenthető lemezre (az eredeti fordítási egység felülírásával) a commitWorkingCopy metódus használatával.
Példaként az alábbi kódrészlet létrehoz egy munkamásolatot a fordítási egységről egy egyéni munkamásolat tulajdonos használatával. A részlet módosítja a puffert, összehangolja a változásokat, véglegesíti a változásokat a lemezen, majd megszünteti a munkamásolatot.
// Eredeti fordítási egység megadása ICompilationUnit originalUnit = ...; // Munkamásolat tulajdonosának megadása WorkingCopyOwner owner = ...; // Munkamásolat létrehozása ICompilationUnit workingCopy = originalUnit.getWorkingCopy(owner, null, null); // Puffer módosítása és egyeztetése IBuffer buffer = ((IOpenable)workingCopy).getBuffer(); buffer.append("class X {}"); workingCopy.reconcile(NO_AST, false, null, null); // Módosítások véglegesítése workingCopy.commitWorkingCopy(false, null); // Munkamásolat megsemmisítése workingCopy.discardWorkingCopy();
Munkamásolatok megoszthatók számos ügyfél között egy munkamásolat tulajdonos használatával. A munkamásolat későbbiekben visszakereshető a findWorkingCopy metódus használatával. Így a megosztott munkamásolat az eredeti fordítási egységhez és a munkamásolat tulajdonosához is egyaránt rögzítve van.
A következő példában az 1. ügyfél létrehoz egy megosztott munkamásolatot, a 2. ügyfél visszakeresi ezt a munkamásolatot, az 1. ügyfél megszünteti, majd a 2 ügyfél megpróbálja a megosztott munkamásolatot visszakeresni és felismeri, hogy az már nem létezik.
// 1. & 2. ügyfél: Az eredeti fordítási egység megadása ICompilationUnit originalUnit = ...; // 1. & 2. ügyfél: Munkamásolat tulajdonos megadása WorkingCopyOwner owner = ...; // 1. ügyfél: Megosztott munkamásolat létrehozása ICompilationUnit workingCopyForClient1 = originalUnit.getWorkingCopy(owner, null, null); // 2. ügyfél: Megosztott munkamásolat visszakeresése ICompilationUnit workingCopyForClient2 = originalUnit.findWorkingCopy(owner); // Ez ugyanaz a munkamásolat assert workingCopyForClient1 == workingCopyForClient2; // 1. ügyfél: megosztott munkamásolat megszüntetése workingCopyForClient1.discardWorkingCopy(); // 2. ügyfél: kísérlet megosztott munkamásolat visszakeresésére, de felismeri, hogy null. workingCopyForClient2 = originalUnit.findWorkingCopy(owner); assert workingCopyForClient2 == null;
Lehetséges CompilationUnit létrehozása nulláról indulva az AST gyári metódusaival. Ezek a metódusnevek a new... taggal kezdődnek. A következő példa egy HelloWorld osztályt hoz létre.
A követező részlet az előállított kimenet:
package example;
import java.util.*;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello" + " world");
}
}
A következő részlet a kimenetet előállító, vonatkozó kód.
AST ast = new AST();
CompilationUnit unit = ast.newCompilationUnit();
PackageDeclaration packageDeclaration = ast.newPackageDeclaration();
packageDeclaration.setName(ast.newSimpleName("example"));
unit.setPackage(packageDeclaration);
ImportDeclaration importDeclaration = ast.newImportDeclaration();
QualifiedName name =
ast.newQualifiedName(
ast.newSimpleName("java"),
ast.newSimpleName("util"));
importDeclaration.setName(name);
importDeclaration.setOnDemand(true);
unit.imports().add(importDeclaration);
TypeDeclaration type = ast.newTypeDeclaration();
type.setInterface(false);
type.setModifiers(Modifier.PUBLIC);
type.setName(ast.newSimpleName("HelloWorld"));
MethodDeclaration methodDeclaration = ast.newMethodDeclaration();
methodDeclaration.setConstructor(false);
methodDeclaration.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
methodDeclaration.setName(ast.newSimpleName("main"));
methodDeclaration.setReturnType(ast.newPrimitiveType(PrimitiveType.VOID));
SingleVariableDeclaration variableDeclaration = ast.newSingleVariableDeclaration();
variableDeclaration.setModifiers(Modifier.NONE);
variableDeclaration.setType(ast.newArrayType(ast.newSimpleType(ast.newSimpleName("String"))));
variableDeclaration.setName(ast.newSimpleName("args"));
methodDeclaration.parameters().add(variableDeclaration);
org.eclipse.jdt.core.dom.Block block = ast.newBlock();
MethodInvocation methodInvocation = ast.newMethodInvocation();
name =
ast.newQualifiedName(
ast.newSimpleName("System"),
ast.newSimpleName("out"));
methodInvocation.setExpression(name);
methodInvocation.setName(ast.newSimpleName("println"));
InfixExpression infixExpression = ast.newInfixExpression();
infixExpression.setOperator(InfixExpression.Operator.PLUS);
StringLiteral literal = ast.newStringLiteral();
literal.setLiteralValue("Hello");
infixExpression.setLeftOperand(literal);
literal = ast.newStringLiteral();
literal.setLiteralValue(" world");
infixExpression.setRightOperand(literal);
methodInvocation.arguments().add(infixExpression);
ExpressionStatement expressionStatement = ast.newExpressionStatement(methodInvocation);
block.statements().add(expressionStatement);
methodDeclaration.setBody(block);
type.bodyDeclarations().add(methodDeclaration);
unit.types().add(type);
private int[] getOperatorPosition(Expression expression, char[] source) { if (expression instanceof InstanceofExpression) { IScanner scanner = ToolFactory.createScanner(false, false, false, false); scanner.setSource(source); int start = expression.getStartPosition(); int end = start + expression.getLength(); scanner.resetTo(start, end); int token; try { while ((token = scanner.getNextToken()) != ITerminalSymbols.TokenNameEOF) { switch(token) { case ITerminalSymbols.TokenNameinstanceof: return new int[] {scanner.getCurrentTokenStartPosition(), scanner.getCurrentTokenEndPosition()}; } } } catch (InvalidInputException e) { } } return null; }Az IScanner metódus a bemeneti forrást felosztja jelsorokká. Minden jelsornak adott értéke van, amint az a ITerminalSymbols felületben meg van határozva. A megfelelő jelsor iterálása és visszakeresése nagyon egyszerű. A super kulcsszó megtalálásához a SuperMethodInvocation kifejezésben a kereső ajánlott.
Bizonyos forráskód módosítások nem biztosítottak a Java elem API felületén keresztül. A forráskód szerkesztésének ennél általánosabb módszere (úgy mint a már meglévő elemek forráskódjának megváltoztatása) a fordítási egység nyers forráskódjának és a DOM/AST újraíró API felületének használata.
A DOM/AST újraíráshoz két készlet API áll rendelkezésre: a leíró újraírás és a módosító újraírás.
A leíró API nem módosítja az AST fát, hanem az ASTRewrite API felületet használja a módosítások leírásának elkészítéséhez.
Az AST újraíró összegyűjti a csomópont módosítások leírásait és lefordítja
olyan szövegszerkesztésekre, amely azután alkalmazható az eredeti forrásra.
// dokumentum létrehozása
ICompilationUnit cu = ... ; // tartalma: "public class X {\n}"
String source = cu.getBuffer().getContents();
Document document= new Document(source);
// DOM/AST létrehozása ICompilationUnit objektumból
ASTParser parser = ASTParser.newParser(AST.JLS2);
parser.setSource(cu);
CompilationUnit astRoot = (CompilationUnit) parser.createAST(null);
// ASTRewrite létrehozása
ASTRewrite rewrite = new ASTRewrite(astRoot.getAST());
// a változás leírása
SimpleName oldName = ((TypeDeclaration)astRoot.types().get(0)).getName();
SimpleName newName = astRoot.getAST().newSimpleName("Y");
rewrite.replace(oldName, newName, null);
// szövegszerkesztések kiszámítása
TextEdit edits = rewrite.rewriteAST(document, cu.getJavaProject().getOptions(true));
// új forráskód kiszámítása
edits.apply(document);
String newSource = document.get();
// fordítási egység frissítése
cu.getBuffer().setContents(newSource);
A módosító API lehetővé teszi az AST közvetlen változtatását:
// dokumentum létrehozása ICompilationUnit cu = ... ; // tartalma: "public class X {\n}" String source = cu.getBuffer().getContents(); Document document= new Document(source); // DOM/AST létrehozása ICompilationUnit objektumból ASTParser parser = ASTParser.newParser(AST.JLS2); parser.setSource(cu); CompilationUnit astRoot = (CompilationUnit) parser.createAST(null); // módosítások rögzítésének indítása astRoot.recordModifications(); // AST módosítása TypeDeclaration typeDeclaration = (TypeDeclaration)astRoot.types().get(0) SimpleName newName = astRoot.getAST().newSimpleName("Y"); typeDeclaration.setName(newName); // szövegszerkesztések kiszámítása TextEdit edits = astRoot.rewrite(document, cu.getJavaProject().getOptions(true)); // új forráskód kiszámítása edits.apply(document); String newSource = document.get(); // fordítási egység frissítése cu.getBuffer().setContents(newSource);
Ha a bedolgozónak tudnia kell Java elemek megváltozásáról, akkor bejegyezhet egy Java IElementChangedListener felületet a JavaCore osztállyal.
JavaCore.addElementChangedListener(new MyJavaElementChangeReporter());
Ha meg szeretné határozni, hogy milyen típusú események figyelése szükséges, akkor használja az addElementChangedListener(IElementChangedListener, int) osztályt.
Például, ha csak egyeztetési műveletek eseményeinek figyelése érdekli:
JavaCore.addElementChangedListener(new MyJavaElementChangeReporter(), ElementChangedEvent.POST_RECONCILE);
A JavaCore kétféle eseményt támogat:
A Java elem változás figyelők lényegüket tekintve hasonlóak a forrásváltozás figyelőkhöz (leírás: forrásváltozások nyomkövetése). A következő programrészlet egy Java elem változásjelentőt valósít meg, amely a rendszerkonzolra nyomtatja az elemkülönbségeket.
public class MyJavaElementChangeReporter implements IElementChangedListener { public void elementChanged(ElementChangedEvent event) { IJavaElementDelta delta= event.getDelta(); if (delta != null) { System.out.println("delta received: "); System.out.print(delta); } } }
Az IJavaElementDelta része a megváltozott elem, és a lezajlott változás jellemzőket leíró jelzők. A különbségfa kiindulási pontja leggyakrabban a Java modell szint. Az ügyfeleknek ezt a különbséget kell navigálniuk a getAffectedChildren használatával, hogy megtudják milyen projektek változtak.
A következő példametódus átvizsgál egy különbséget, majd kinyomtatja a hozzáadott, eltávolított, módosított elemeket:
void traverseAndPrint(IJavaElementDelta delta) { switch (delta.getKind()) { case IJavaElementDelta.ADDED: System.out.println(delta.getElement() + " hozzá lett adva"); break; case IJavaElementDelta.REMOVED: System.out.println(delta.getElement() + " eltávolításra került"); break; case IJavaElementDelta.CHANGED: System.out.println(delta.getElement() + " módosult"); if ((delta.getFlags() & IJavaElementDelta.F_CHILDREN) != 0) { System.out.println("The change was in its children"); } if ((delta.getFlags() & IJavaElementDelta.F_CONTENT) != 0) { System.out.println("The change was in its content"); } /* Más jelzőket is ellenőrízhet */ break; } IJavaElementDelta[] children = delta.getAffectedChildren(); for (int i = 0; i < children.length; i++) { traverseAndPrint(children[i]); } }
Több fajta művelet indíthat el Java elem változási értesítést. Álljon itt néhány példa:
Az IResourceDelta felülethez hasonlóan a Java elem különbségek kötegelhetőek az IWorkspaceRunnable használatával. Számos Java modell művelet eredményeként kapott, az IWorkspaceRunnable felületen futó különbségek összevonásra kerülnek és egy jelentésben jellenek meg.
A JavaCore biztosít futási metódust Java elem változások kötegelésére.
Például a következő kódtöredék 2 Java elem változási eseményt fog aktiválni:
// Csomag megadása IPackageFragment pkg = ...; // két fordítási egység létrehozása ICompilationUnit unitA = pkg.createCompilationUnit("A.java", "public class A {}", false, null); ICompilationUnit unitB = pkg.createCompilationUnit("B.java", "public class B {}", false, null);
Míg a következő kódtöredék 1 Java elem változási eseményt fog aktiválni:
// Csomag megadása IPackageFragment pkg = ...; // két fordítási egység létrehozása JavaCore.run( new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { ICompilationUnit unitA = pkg.createCompilationUnit("A.java", "public class A {}", false, null); ICompilationUnit unitB = pkg.createCompilationUnit("B.java", "public class B {}", false, null); } }, null);