Quellcode durchsuchen

318241: resolving annotation values too early

tags/V1_6_9RC2
aclement vor 14 Jahren
Ursprung
Commit
7a2f0c64f6

+ 3
- 0
tests/bugs169/pr318241/Pk.java Datei anzeigen

package somepackage;

public class Pk {}

+ 8
- 0
tests/bugs169/pr318241/PkItd.aj Datei anzeigen

package somepackage;

privileged aspect PkItd {

declare parents: Pk implements java.io.Serializable;

private static final long Pk.serialVersionUID = -3602111784930992656L;
}

+ 13
- 0
tests/bugs169/pr318241/SomeAnnotation.java Datei anzeigen

package somepackage;

import java.io.Serializable;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface SomeAnnotation {
Class<? extends Serializable> value() default Long.class;
}

+ 5
- 0
tests/bugs169/pr318241/SomeClass.java Datei anzeigen

package somepackage;

@SomeAnnotation(value = Pk.class)
public class SomeClass {
}

+ 10
- 0
tests/bugs169/pr318241/Two.aj Datei anzeigen

import java.lang.annotation.*;

public aspect Two {
declare parents: @Foo * implements II;
}

interface II {}

@Retention(RetentionPolicy.RUNTIME)
@interface Foo {}

+ 4
- 0
tests/src/org/aspectj/systemtest/ajc169/Ajc169Tests.java Datei anzeigen



public class Ajc169Tests extends org.aspectj.testing.XMLBasedAjcTestCase { public class Ajc169Tests extends org.aspectj.testing.XMLBasedAjcTestCase {


public void testMessyDecp_318241() {
runTest("messy decp");
}

// public void testMultiAnnosRunning_pr315820_1() { // public void testMultiAnnosRunning_pr315820_1() {
// runTest("multiple annos running - 1"); // runTest("multiple annos running - 1");
// } // }

+ 5
- 0
tests/src/org/aspectj/systemtest/ajc169/ajc169.xml Datei anzeigen



<suite> <suite>


<ajc-test dir="bugs169/pr318241" title="messy decp">
<compile files="PkItd.aj Two.aj SomeClass.java Pk.java SomeAnnotation.java" options="-1.5">
</compile>
</ajc-test>

<ajc-test dir="bugs169/pr287613" title="compound declare patterns - method - 2"> <ajc-test dir="bugs169/pr287613" title="compound declare patterns - method - 2">
<compile files="DAMethod2.java" options="-1.5 -showWeaveInfo"> <compile files="DAMethod2.java" options="-1.5 -showWeaveInfo">
<message kind="weave" text="'public void Person.foo()' (DAMethod2.java:11) is annotated with @Annot method annotation from 'DAMethod2' (DAMethod2.java:26)"/> <message kind="weave" text="'public void Person.foo()' (DAMethod2.java:11) is annotated with @Annot method annotation from 'DAMethod2' (DAMethod2.java:26)"/>

+ 41
- 26
tests/src/org/aspectj/systemtest/incremental/tools/AjdeInteractionTestbed.java Datei anzeigen

} }


public static void configureInPath(String projectName, File inpath) { public static void configureInPath(String projectName, File inpath) {
Set s = new HashSet();
Set<File> s = new HashSet<File>();
s.add(inpath); s.add(inpath);
AjCompiler compiler = CompilerFactory.getCompilerForProjectWithDir(sandboxDir + File.separator + projectName); AjCompiler compiler = CompilerFactory.getCompilerForProjectWithDir(sandboxDir + File.separator + projectName);
((MultiProjTestCompilerConfiguration) compiler.getCompilerConfiguration()).setInpath(s); ((MultiProjTestCompilerConfiguration) compiler.getCompilerConfiguration()).setInpath(s);
collectUpFiles(projectBase, projectBase, filesForCompilation); collectUpFiles(projectBase, projectBase, filesForCompilation);
boolean changed = false; boolean changed = false;
for (int i = 0; i < filesForCompilation.size(); i++) { for (int i = 0; i < filesForCompilation.size(); i++) {
if (!currentFiles.contains(filesForCompilation.get(i)))
if (!currentFiles.contains(filesForCompilation.get(i))) {
changed = true; changed = true;
}
} }
for (int i = 0; i < currentFiles.size(); i++) { for (int i = 0; i < currentFiles.size(); i++) {
if (!filesForCompilation.contains(currentFiles.get(i)))
if (!filesForCompilation.contains(currentFiles.get(i))) {
changed = true; changed = true;
}
} }
if (changed) { if (changed) {
((MultiProjTestCompilerConfiguration) icc).setProjectSourceFiles(filesForCompilation); ((MultiProjTestCompilerConfiguration) icc).setProjectSourceFiles(filesForCompilation);
collectUpXmlFiles(projectBase, projectBase, collector); collectUpXmlFiles(projectBase, projectBase, collector);
boolean changed = false; boolean changed = false;
for (int i = 0; i < collector.size(); i++) { for (int i = 0; i < collector.size(); i++) {
if (!currentXmlFiles.contains(collector.get(i)))
if (!currentXmlFiles.contains(collector.get(i))) {
changed = true; changed = true;
}
} }
for (int i = 0; i < currentXmlFiles.size(); i++) { for (int i = 0; i < currentXmlFiles.size(); i++) {
if (!collector.contains(currentXmlFiles.get(i)))
if (!collector.contains(currentXmlFiles.get(i))) {
changed = true; changed = true;
}
} }
if (changed) { if (changed) {
((MultiProjTestCompilerConfiguration) icc).setProjectXmlConfigFiles(collector); ((MultiProjTestCompilerConfiguration) icc).setProjectXmlConfigFiles(collector);


private void collectUpFiles(File location, File base, List collectionPoint) { private void collectUpFiles(File location, File base, List collectionPoint) {
String contents[] = location.list(); String contents[] = location.list();
if (contents == null)
if (contents == null) {
return; return;
}
for (int i = 0; i < contents.length; i++) { for (int i = 0; i < contents.length; i++) {
String string = contents[i]; String string = contents[i];
File f = new File(location, string); File f = new File(location, string);


private void collectUpXmlFiles(File location, File base, List collectionPoint) { private void collectUpXmlFiles(File location, File base, List collectionPoint) {
String contents[] = location.list(); String contents[] = location.list();
if (contents == null)
if (contents == null) {
return; return;
}
for (int i = 0; i < contents.length; i++) { for (int i = 0; i < contents.length; i++) {
String string = contents[i]; String string = contents[i];
File f = new File(location, string); File f = new File(location, string);
MultiProjTestMessageHandler handler = (MultiProjTestMessageHandler) compiler.getMessageHandler(); MultiProjTestMessageHandler handler = (MultiProjTestMessageHandler) compiler.getMessageHandler();
if (handler.hasErrorMessages()) { if (handler.hasErrorMessages()) {
System.err.println("Build errors:"); System.err.println("Build errors:");
for (Iterator iter = handler.getErrorMessages().iterator(); iter.hasNext();) {
IMessage element = (IMessage) iter.next();
for (Iterator<IMessage> iter = handler.getErrorMessages().iterator(); iter.hasNext();) {
IMessage element = iter.next();
System.err.println(element); System.err.println(element);
} }
System.err.println("---------"); System.err.println("---------");
} }
} }


public List getErrorMessages(String projectName) {
public List<IMessage> getErrorMessages(String projectName) {
AjCompiler compiler = CompilerFactory.getCompilerForProjectWithDir(sandboxDir + File.separator + projectName); AjCompiler compiler = CompilerFactory.getCompilerForProjectWithDir(sandboxDir + File.separator + projectName);
return ((MultiProjTestMessageHandler) compiler.getMessageHandler()).getErrorMessages(); return ((MultiProjTestMessageHandler) compiler.getMessageHandler()).getErrorMessages();
} }


public List getWarningMessages(String projectName) {
public List<IMessage> getWarningMessages(String projectName) {
AjCompiler compiler = CompilerFactory.getCompilerForProjectWithDir(sandboxDir + File.separator + projectName); AjCompiler compiler = CompilerFactory.getCompilerForProjectWithDir(sandboxDir + File.separator + projectName);
return ((MultiProjTestMessageHandler) compiler.getMessageHandler()).getWarningMessages(); return ((MultiProjTestMessageHandler) compiler.getMessageHandler()).getWarningMessages();
} }


public List getWeavingMessages(String projectName) {
public List<IMessage> getWeavingMessages(String projectName) {
AjCompiler compiler = CompilerFactory.getCompilerForProjectWithDir(sandboxDir + File.separator + projectName); AjCompiler compiler = CompilerFactory.getCompilerForProjectWithDir(sandboxDir + File.separator + projectName);
return ((MultiProjTestMessageHandler) compiler.getMessageHandler()).getWeavingMessages(); return ((MultiProjTestMessageHandler) compiler.getMessageHandler()).getWeavingMessages();
} }


public void checkForError(String projectName, String anError) { public void checkForError(String projectName, String anError) {
AjCompiler compiler = CompilerFactory.getCompilerForProjectWithDir(sandboxDir + File.separator + projectName); AjCompiler compiler = CompilerFactory.getCompilerForProjectWithDir(sandboxDir + File.separator + projectName);
List messages = ((MultiProjTestMessageHandler) compiler.getMessageHandler()).getErrorMessages();
for (Iterator iter = messages.iterator(); iter.hasNext();) {
IMessage element = (IMessage) iter.next();
if (element.getMessage().indexOf(anError) != -1)
List<IMessage> messages = ((MultiProjTestMessageHandler) compiler.getMessageHandler()).getErrorMessages();
for (Iterator<IMessage> iter = messages.iterator(); iter.hasNext();) {
IMessage element = iter.next();
if (element.getMessage().indexOf(anError) != -1) {
return; return;
}
} }
fail("Didn't find the error message:\n'" + anError + "'.\nErrors that occurred:\n" + messages); fail("Didn't find the error message:\n'" + anError + "'.\nErrors that occurred:\n" + messages);
} }
*/ */
public String printCompiledAndWovenFiles(String projectName) { public String printCompiledAndWovenFiles(String projectName) {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
if (getCompiledFiles(projectName).size() == 0 && getWovenClasses(projectName).size() == 0)
if (getCompiledFiles(projectName).size() == 0 && getWovenClasses(projectName).size() == 0) {
sb.append("No files were compiled or woven\n"); sb.append("No files were compiled or woven\n");
}
for (Iterator iter = getCompiledFiles(projectName).iterator(); iter.hasNext();) { for (Iterator iter = getCompiledFiles(projectName).iterator(); iter.hasNext();) {
Object element = iter.next(); Object element = iter.next();
sb.append("compiled: " + element + "\n"); sb.append("compiled: " + element + "\n");
for (Iterator iter = woven.iterator(); iter.hasNext();) { for (Iterator iter = woven.iterator(); iter.hasNext();) {
System.out.println(" :" + iter.next()); System.out.println(" :" + iter.next());
} }
if (wasFullBuild())
if (wasFullBuild()) {
System.out.println("It was a batch (full) build"); System.out.println("It was a batch (full) build");
}
System.out.println("============================================="); System.out.println("=============================================");
} }


* Check we compiled/wove the right number of files, passing '-1' indicates you don't care about that number. * Check we compiled/wove the right number of files, passing '-1' indicates you don't care about that number.
*/ */
public void checkCompileWeaveCount(String projectName, int expCompile, int expWoven) { public void checkCompileWeaveCount(String projectName, int expCompile, int expWoven) {
if (expCompile != -1 && getCompiledFiles(projectName).size() != expCompile)
if (expCompile != -1 && getCompiledFiles(projectName).size() != expCompile) {
fail("Expected compilation of " + expCompile + " files but compiled " + getCompiledFiles(projectName).size() + "\n" fail("Expected compilation of " + expCompile + " files but compiled " + getCompiledFiles(projectName).size() + "\n"
+ printCompiledAndWovenFiles(projectName)); + printCompiledAndWovenFiles(projectName));
if (expWoven != -1 && getWovenClasses(projectName).size() != expWoven)
}
if (expWoven != -1 && getWovenClasses(projectName).size() != expWoven) {
fail("Expected weaving of " + expWoven + " files but wove " + getWovenClasses(projectName).size() + "\n" fail("Expected weaving of " + expWoven + " files but wove " + getWovenClasses(projectName).size() + "\n"
+ printCompiledAndWovenFiles(projectName)); + printCompiledAndWovenFiles(projectName));
}
} }


public void checkWasntFullBuild() { public void checkWasntFullBuild() {
return ((MultiProjTestBuildProgressMonitor) compiler.getBuildProgressMonitor()).getTimeTaken(); return ((MultiProjTestBuildProgressMonitor) compiler.getBuildProgressMonitor()).getTimeTaken();
} }


public List getCompiledFiles(String projectName) {
public List<String> getCompiledFiles(String projectName) {
AjCompiler compiler = CompilerFactory.getCompilerForProjectWithDir(sandboxDir + File.separator + projectName); AjCompiler compiler = CompilerFactory.getCompilerForProjectWithDir(sandboxDir + File.separator + projectName);
return ((MultiProjTestBuildProgressMonitor) compiler.getBuildProgressMonitor()).getCompiledFiles(); return ((MultiProjTestBuildProgressMonitor) compiler.getBuildProgressMonitor()).getCompiledFiles();
} }
return compiler.getModel(); return compiler.getModel();
} }


public List getWovenClasses(String projectName) {
public List<String> getWovenClasses(String projectName) {
AjCompiler compiler = CompilerFactory.getCompilerForProjectWithDir(sandboxDir + File.separator + projectName); AjCompiler compiler = CompilerFactory.getCompilerForProjectWithDir(sandboxDir + File.separator + projectName);
return ((MultiProjTestBuildProgressMonitor) compiler.getBuildProgressMonitor()).getWovenClasses(); return ((MultiProjTestBuildProgressMonitor) compiler.getBuildProgressMonitor()).getWovenClasses();
} }
// Infrastructure below here // Infrastructure below here


private static void log(String msg) { private static void log(String msg) {
if (VERBOSE)
if (VERBOSE) {
System.out.println(msg); System.out.println(msg);
}
} }


private static void lognoln(String msg) { private static void lognoln(String msg) {
if (VERBOSE)
if (VERBOSE) {
System.out.print(msg); System.out.print(msg);
}
} }


/** Return the *full* path to this file which is taken relative to the project dir */ /** Return the *full* path to this file which is taken relative to the project dir */
informedAboutKindOfBuild = false; informedAboutKindOfBuild = false;
decisions = new StringBuffer(); decisions = new StringBuffer();
fullBuildOccurred = false; fullBuildOccurred = false;
if (detectedDeletions != null)
if (detectedDeletions != null) {
detectedDeletions.clear(); detectedDeletions.clear();
}
} }


public boolean pathChange = false; public boolean pathChange = false;
} }


public static boolean wasFullBuild() { public static boolean wasFullBuild() {
if (!informedAboutKindOfBuild)
if (!informedAboutKindOfBuild) {
throw new RuntimeException("I never heard about what kind of build it was!!"); throw new RuntimeException("I never heard about what kind of build it was!!");
}
return fullBuildOccurred; return fullBuildOccurred;
} }



+ 26
- 20
tests/src/org/aspectj/systemtest/incremental/tools/MultiProjTestBuildProgressMonitor.java Datei anzeigen

import org.aspectj.ajde.core.IBuildProgressMonitor; import org.aspectj.ajde.core.IBuildProgressMonitor;


/** /**
* IBuildProgressMonitor that records how many files were compiled and
* woven as well as whether or not the build was a full build. Will print
* progress information to the screen if VERBOSE is true.
* IBuildProgressMonitor that records how many files were compiled and woven as well as whether or not the build was a full build.
* Will print progress information to the screen if VERBOSE is true.
*/ */
public class MultiProjTestBuildProgressMonitor implements IBuildProgressMonitor { public class MultiProjTestBuildProgressMonitor implements IBuildProgressMonitor {


public boolean VERBOSE = false; public boolean VERBOSE = false;
private List compiledFiles=new ArrayList();
private List wovenClasses=new ArrayList();
private List<String> compiledFiles = new ArrayList<String>();
private List<String> wovenClasses = new ArrayList<String>();
private long starttime = 0; private long starttime = 0;
private long totaltimetaken = 0; private long totaltimetaken = 0;
private boolean wasFullBuild = true; private boolean wasFullBuild = true;
public void finish(boolean wasFullBuild) { public void finish(boolean wasFullBuild) {
log("IBuildProgressMonitor.finish(" + wasFullBuild + ")"); log("IBuildProgressMonitor.finish(" + wasFullBuild + ")");
this.wasFullBuild = wasFullBuild; this.wasFullBuild = wasFullBuild;
totaltimetaken=(System.currentTimeMillis()-starttime);
totaltimetaken = (System.currentTimeMillis() - starttime);
} }


public boolean isCancelRequested() { public boolean isCancelRequested() {
} }


public void setProgress(double percentDone) { public void setProgress(double percentDone) {
log("IBuildProgressMonitor.setProgress("+percentDone+")");
log("IBuildProgressMonitor.setProgress(" + percentDone + ")");
} }


public void setProgressText(String text) { public void setProgressText(String text) {
log("BuildProgressMonitor.setProgressText("+text+")");
log("BuildProgressMonitor.setProgressText(" + text + ")");
if (text.startsWith("compiled: ")) { if (text.startsWith("compiled: ")) {
compiledFiles.add(text.substring(10)); compiledFiles.add(text.substring(10));
} else if (text.startsWith("woven class ")) { } else if (text.startsWith("woven class ")) {
wovenClasses.add(text.substring(12));
wovenClasses.add(text.substring(12));
} else if (text.startsWith("woven aspect ")) { } else if (text.startsWith("woven aspect ")) {
wovenClasses.add(text.substring(13)); wovenClasses.add(text.substring(13));
}
}
} }


public void begin() { public void begin() {
starttime = System.currentTimeMillis(); starttime = System.currentTimeMillis();
log("IBuildProgressMonitor.start()");
log("IBuildProgressMonitor.start()");
}

public List<String> getCompiledFiles() {
return compiledFiles;
} }


public List getCompiledFiles() { return compiledFiles;}
public List getWovenClasses() { return wovenClasses; }
public List<String> getWovenClasses() {
return wovenClasses;
}


public void log(String s) { public void log(String s) {
if (VERBOSE) System.out.println(s);
if (VERBOSE) {
System.out.println(s);
}
} }
public long getTimeTaken() { public long getTimeTaken() {
return totaltimetaken; return totaltimetaken;
} }
public boolean wasFullBuild() { public boolean wasFullBuild() {
return wasFullBuild; return wasFullBuild;
} }
public void reset() { public void reset() {
wasFullBuild=true;
wasFullBuild = true;
compiledFiles.clear(); compiledFiles.clear();
wovenClasses.clear(); wovenClasses.clear();
} }

+ 60
- 53
tests/src/org/aspectj/systemtest/incremental/tools/MultiProjTestMessageHandler.java Datei anzeigen

import org.aspectj.bridge.IMessage.Kind; import org.aspectj.bridge.IMessage.Kind;


/** /**
* IMessageHandler which by default ignores INFO and WEAVEINFO messages.
* Records the warning, weaving, compiler errors and error messages and
* provides methods to get them.
* IMessageHandler which by default ignores INFO and WEAVEINFO messages. Records the warning, weaving, compiler errors and error
* messages and provides methods to get them.
*/ */
public class MultiProjTestMessageHandler implements IBuildMessageHandler { public class MultiProjTestMessageHandler implements IBuildMessageHandler {


private final boolean VERBOSE = false; private final boolean VERBOSE = false;
private boolean receivedNonIncrementalBuildMessage = false; private boolean receivedNonIncrementalBuildMessage = false;
private boolean receivedBatchBuildMessage = false; private boolean receivedBatchBuildMessage = false;
private List errorMessages;
private List warningMessages;
private List weavingMessages;
private List<IMessage> errorMessages;
private List<IMessage> warningMessages;
private List<IMessage> weavingMessages;
private List compilerErrors; private List compilerErrors;
private List ignoring; private List ignoring;
public MultiProjTestMessageHandler() { public MultiProjTestMessageHandler() {
ignoring = new ArrayList(); ignoring = new ArrayList();
errorMessages = new ArrayList();
warningMessages = new ArrayList();
weavingMessages = new ArrayList();
errorMessages = new ArrayList<IMessage>();
warningMessages = new ArrayList<IMessage>();
weavingMessages = new ArrayList<IMessage>();
compilerErrors = new ArrayList(); compilerErrors = new ArrayList();
ignore(IMessage.INFO);
ignore(IMessage.WEAVEINFO);
ignore(IMessage.INFO);
ignore(IMessage.WEAVEINFO);
} }
public boolean handleMessage(IMessage message) throws AbortException { public boolean handleMessage(IMessage message) throws AbortException {
IMessage.Kind kind = message.getKind();
if (isIgnoring(kind)) {
return true;
}
if (kind.equals(IMessage.ABORT) || message.getThrown() != null) {
log("> AjCompiler error: "+message.getMessage() +", " + message.getThrown() + ")"); //$NON-NLS-1$
message.getThrown().printStackTrace();
compilerErrors.add(message + ", (" + message.getThrown() +")");
if (VERBOSE && (message.getThrown() != null)) message.getThrown().printStackTrace();
IMessage.Kind kind = message.getKind();
if (isIgnoring(kind)) {
return true; return true;
}
if (message.getKind()==IMessage.ERROR) errorMessages.add(message);
if (message.getKind()==IMessage.WARNING) warningMessages.add(message);
if (message.getKind()==IMessage.WEAVEINFO) weavingMessages.add(message);
log("IMessageHandler.handleMessage("+message+")");
}
if (kind.equals(IMessage.ABORT) || message.getThrown() != null) {
log("> AjCompiler error: " + message.getMessage() + ", " + message.getThrown() + ")"); //$NON-NLS-1$
message.getThrown().printStackTrace();
compilerErrors.add(message + ", (" + message.getThrown() + ")");
if (VERBOSE && (message.getThrown() != null)) {
message.getThrown().printStackTrace();
}
return true;
}
if (message.getKind() == IMessage.ERROR) {
errorMessages.add(message);
}
if (message.getKind() == IMessage.WARNING) {
warningMessages.add(message);
}
if (message.getKind() == IMessage.WEAVEINFO) {
weavingMessages.add(message);
}
log("IMessageHandler.handleMessage(" + message + ")");
return true; return true;
} }


public void dontIgnore(Kind kind) { public void dontIgnore(Kind kind) {
if (null != kind) {
ignoring.remove(kind);
}
if (null != kind) {
ignoring.remove(kind);
}
} }


public boolean isIgnoring(Kind kind) { public boolean isIgnoring(Kind kind) {
return ((null != kind) && (ignoring.contains(kind))); return ((null != kind) && (ignoring.contains(kind)));
} }
public void ignore(Kind kind) { public void ignore(Kind kind) {
if ((null != kind) && (!ignoring.contains(kind))) {
ignoring.add(kind);
}
if ((null != kind) && (!ignoring.contains(kind))) {
ignoring.add(kind);
}
} }
public boolean hasWarning() { public boolean hasWarning() {
return !warningMessages.isEmpty(); return !warningMessages.isEmpty();
} }
public boolean hasErrorMessages() { public boolean hasErrorMessages() {
return !errorMessages.isEmpty(); return !errorMessages.isEmpty();
} }
public boolean hasCompilerErrorMessages() { public boolean hasCompilerErrorMessages() {
return !compilerErrors.isEmpty(); return !compilerErrors.isEmpty();
} }
public List/*IMessage*/ getErrorMessages() {
public List<IMessage> getErrorMessages() {
return errorMessages; return errorMessages;
} }
public List/*IMessage*/ getWarningMessages() {
public List<IMessage> getWarningMessages() {
return warningMessages; return warningMessages;
} }
public List/*IMessage*/ getWeavingMessages() {
public List<IMessage> getWeavingMessages() {
return weavingMessages; return weavingMessages;
} }
public List/*IMessage*/ getCompilerErrors() {
public List/* IMessage */getCompilerErrors() {
return compilerErrors; return compilerErrors;
} }

public void log(String s) { public void log(String s) {
if (VERBOSE) System.out.println(s);
if (VERBOSE) {
System.out.println(s);
}
} }


public void reset() { public void reset() {
receivedNonIncrementalBuildMessage=false;
receivedBatchBuildMessage=false;
receivedNonIncrementalBuildMessage = false;
receivedBatchBuildMessage = false;
errorMessages.clear(); errorMessages.clear();
warningMessages.clear(); warningMessages.clear();
weavingMessages.clear(); weavingMessages.clear();

Laden…
Abbrechen
Speichern