Merge branch 'master' into jdk8. Also added some major fixes whilst merging.

Conflicts:
	src/core/lombok/javac/handlers/JavacHandlerUtil.java
	src/utils/lombok/javac/CommentCatcher.java
	src/utils/lombok/javac/Javac.java
This commit is contained in:
Reinier Zwitserloot committed 2013-07-22 23:23:46 +02:00
commit 45697b5081
219 files changed
+6135 -982

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (C) 2009-2011 The Project Lombok Authors.
Copyright (C) 2009-2013 The Project Lombok Authors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+7 -13
View File
@@ -27,7 +27,7 @@ the common tasks and can be called on to run the main aspects of all the sub-scr
<property name="credentialsFile" value="google.properties" />
<property name="build.compiler" value="javac1.6" />
<property name="ivy.retrieve.pattern" value="lib/[conf]/[artifact].[ext]" />
<property name="ivy.retrieve.pattern" value="lib/[conf]/[organisation]-[artifact].[ext]" />
<available file="lib/ivyplusplus.jar" property="ivyplusplus.available" />
<path id="build.path">
@@ -81,7 +81,7 @@ the common tasks and can be called on to run the main aspects of all the sub-scr
<target name="load-ipp" depends="download-ipp">
<taskdef classpath="lib/ivyplusplus.jar" resource="com/zwitserloot/ivyplusplus/antlib.xml" uri="antlib:com.zwitserloot.ivyplusplus" />
<ivy:ensureippversion version="1.7" property="ivyplusplus.minimumAvailable" />
<ivy:ensureippversion version="1.12" property="ivyplusplus.minimumAvailable" />
</target>
<target name="redownload-ipp" unless="ivyplusplus.minimumAvailable">
@@ -147,6 +147,7 @@ the common tasks and can be called on to run the main aspects of all the sub-scr
</ivy:compile>
<ivy:compile destdir="build/stubs" source="1.5" target="1.5" includeantruntime="false">
<src path="src/stubs" />
<src path="src/javac-only-stubs" />
<classpath location="build/stubsstubs" />
</ivy:compile>
@@ -196,7 +197,7 @@ the common tasks and can be called on to run the main aspects of all the sub-scr
<target name="dist" description="Builds THE lombok.jar file which contains everything." depends="version, compile">
<mkdir dir="dist" />
<copy file="doc/changelog.markdown" tofile="build/changelog.txt" />
<taskdef name="jarjar" classname="com.tonicsystems.jarjar.JarJarTask" classpath="lib/build/jarjar.jar" />
<taskdef name="jarjar" classname="com.tonicsystems.jarjar.JarJarTask" classpath="lib/build/com.googlecode.jarjar-jarjar.jar" />
<jarjar destfile="dist/lombok-${lombok.version}.jar">
<fileset dir="build/lombok">
<exclude name="com/sun/tools/javac/**"/>
@@ -255,7 +256,7 @@ the common tasks and can be called on to run the main aspects of all the sub-scr
</target>
<target name="eclipse" depends="deps, contrib" description="Creates eclipse project files and downloads all dependencies. Open this directory as project in eclipse after running this target.">
<ivy:eclipsegen>
<ivy:eclipsegen source="1.6">
<srcdir dir="src/core" />
<srcdir dir="src/utils" />
<srcdir dir="src/eclipseAgent" />
@@ -271,21 +272,14 @@ the common tasks and can be called on to run the main aspects of all the sub-scr
<settings>
<url url="http://projectlombok.org/downloads/lombok.eclipse.settings" />
</settings>
<apt location="lib/build/spi.jar" />
<apt location="lib/build/projectlombok.org-spi.jar" />
</ivy:eclipsegen>
<condition property="startOnFirstThread" value="-XstartOnFirstThread" else="">
<os family="mac" />
</condition>
<copy
file="buildScripts/eclipse-debug-target.template"
tofile="LombokizedEclipse.launch"
preservelastmodified="true"
overwrite="true">
<filterset>
<filter token="START_ON_FIRST_THREAD" value="${startOnFirstThread}" />
</filterset>
</copy>
</target>
@@ -306,7 +300,7 @@ the common tasks and can be called on to run the main aspects of all the sub-scr
</target>
<target name="test-ecj" depends="dist, contrib" unless="tests.skip">
<java jar="lib/ecj/ecj.jar" fork="true" failonerror="true">
<java jar="lib/ecj/org.eclipse.custom-ecj.jar" fork="true" failonerror="true">
<jvmarg value="-javaagent:dist/lombok.jar=ecj" />
<arg value="-source" />
<arg value="1.6" />
+1 -1
View File
@@ -21,7 +21,7 @@
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl}"/>
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms40m -Xmx512m -XX:MaxPermSize=256m @START_ON_FIRST_THREAD@ -Dorg.eclipse.swt.internal.carbon.smallFonts -javaagent:${project_loc:lombok}/dist/lombok.jar -Xbootclasspath/a:${project_loc:lombok}/dist/lombok.jar -Dlombok.patcher.patchDebugDir=${project_loc:lombok}/debug/patchedClasses"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms40m -Xmx512m -XX:MaxPermSize=256m -Dorg.eclipse.swt.internal.carbon.smallFonts -javaagent:${project_loc:lombok}/dist/lombok.jar -Xbootclasspath/a:${project_loc:lombok}/dist/lombok.jar -Dlombok.patcher.patchDebugDir=${project_loc:lombok}/debug/patchedClasses"/>
<stringAttribute key="pde.version" value="3.3"/>
<stringAttribute key="product" value="org.eclipse.sdk.ide"/>
<booleanAttribute key="show_selected_only" value="false"/>
@@ -0,0 +1,14 @@
<ivy-module version="2.0">
<info organisation="org.projectlombok" module="lombok.patcher" revision="0.7" publication="20130708220900">
<license name="MIT License" url="http://www.opensource.org/licenses/mit-license.php" />
<ivyauthor name="rzwitserloot" url="http://zwitserloot.com/" />
<ivyauthor name="rspilker" url="http://github.com/rspilker" />
<description homepage="http://projectlombok.org/" />
</info>
<configurations>
<conf name="default" />
</configurations>
<publications>
<artifact conf="default" url="http://projectlombok.org/downloads/lombok.patcher-0.7.jar" />
</publications>
</ivy-module>
+2 -1
View File
@@ -12,11 +12,12 @@
<conf name="javac7" />
</configurations>
<dependencies>
<dependency org="org.projectlombok" name="lombok.patcher" rev="0.6" conf="build->default; runtime->default" />
<dependency org="org.projectlombok" name="lombok.patcher" rev="0.7" conf="build->default; runtime->default" />
<dependency org="zwitserloot.com" name="cmdreader" rev="1.2" conf="build->runtime; runtime" />
<dependency org="junit" name="junit" rev="4.8.2" conf="test->default; contrib->sources" />
<dependency org="log4j" name="log4j" rev="1.2.16" conf="test->default; contrib->sources" />
<dependency org="org.apache.logging.log4j" name="log4j-api" rev="2.0-beta4" conf="test->default; contrib->sources" />
<dependency org="commons-logging" name="commons-logging" rev="1.1.1" conf="test->default; contrib->sources"/>
<dependency org="org.slf4j" name="slf4j-api" rev="1.6.1" conf="test->default; contrib->sources"/>
<dependency org="org.slf4j" name="slf4j-ext" rev="1.6.1" conf="test->default; contrib->sources"/>
+11 -5
View File
@@ -76,7 +76,7 @@ such as converting the changelog into HTML, and creating javadoc.
</target>
<target name="-website-main" depends="-website-clean, version, javadoc, changelogToHtml">
<taskdef classpath="lib/build/java2html.jar" name="java2html" classname="de.java2html.anttasks.Java2HtmlTask" />
<taskdef classpath="lib/build/de.java2html-java2html.jar" name="java2html" classname="de.java2html.anttasks.Java2HtmlTask" />
<mkdir dir="build/website" />
<copy todir="build/website">
<fileset dir="website">
@@ -142,9 +142,18 @@ such as converting the changelog into HTML, and creating javadoc.
<antcall target="-integrateSnippet">
<param name="transformationName" value="val" />
</antcall>
<antcall target="-integrateSnippet">
<param name="transformationName" value="Value" />
</antcall>
<antcall target="-integrateSnippet">
<param name="transformationName" value="Delegate" />
</antcall>
<antcall target="-integrateSnippet">
<param name="transformationName" value="NonNull" />
</antcall>
<antcall target="-integrateSnippet">
<param name="transformationName" value="experimental/Builder" />
</antcall>
<antcall target="-integrateSnippet">
<param name="transformationName" value="experimental/Accessors" />
</antcall>
@@ -157,9 +166,6 @@ such as converting the changelog into HTML, and creating javadoc.
<antcall target="-integrateSnippet">
<param name="transformationName" value="experimental/Wither" />
</antcall>
<antcall target="-integrateSnippet">
<param name="transformationName" value="experimental/Value" />
</antcall>
<antcall target="-integrateSnippet">
<param name="transformationName" value="experimental/onX" />
</antcall>
@@ -327,7 +333,7 @@ such as converting the changelog into HTML, and creating javadoc.
<link href="http://commons.apache.org/logging/apidocs/" />
<link href="http://logging.apache.org/log4j/1.2/apidocs/" />
<header><![CDATA[<a href='http://projectlombok.org/' target='_blank'>Lombok</a> - ]]>v${lombok.version}</header>
<bottom><![CDATA[<i>Copyright &copy; 2009-2011 The Project Lombok Authors, licensed under the <a href='http://www.opensource.org/licenses/mit-license.php'>MIT licence</a>.]]></bottom>
<bottom><![CDATA[<i>Copyright &copy; 2009-2013 The Project Lombok Authors, licensed under the <a href='http://www.opensource.org/licenses/mit-license.php'>MIT licence</a>.]]></bottom>
</javadoc>
<!-- bugfix for boneheaded javadoc bug where ?is-external=true is inserted before an anchor ref, breaking the anchor ref.
is-external=true doesn't actually do anything, so, we'll just get rid of it. -->
+17 -1
View File
@@ -1,9 +1,25 @@
Lombok Changelog
----------------
### v0.11.7 (Edgy Guinea Pig)
### v0.12.0 "Angry Butterfly" (July 16th, 2013)
* FEATURE: javadoc on fields will now be copied to generated getters / setters / withers. There are ways to specify separate javadoc for the field, the setter, and the getter, and `@param` and `@return` are handled appropriately. Addresses feature request [Issue #59](https://code.google.com/p/projectlombok/issues/detail?id=59). [@Getter and @Setter documentation](http://projectlombok.org/features/GetterSetter.html). [@Wither documentation](http://projectlombok.org/features/experimental/Wither.html).
* CHANGE: The desugaring of @Getter(lazy=true) is now less object creation intensive. Documentation has been updated to reflect what the new desugaring looks like. [@Getter(lazy=true) documentation](http://projectlombok.org/features/GetterLazy.html).
* PROMOTION: `@Value` has been promoted from experimental to the main package with no changes. The 'old' experimental one is still around but is deprecated, and is an alias for the new main package one. [@Value documentation](http://projectlombok.org/features/Value.html).
* FEATURE: {Experimental} `@Builder` support. One of our earliest feature request issues, [Issue #16](https://code.google.com/p/projectlombok/issues/detail?id=16), has finally been addressed. [@Builder documentation](http://projectlombok.org/features/experimental/Builder.html).
* FEATURE: `@NonNull` on a method or constructor parameter now generates a null-check statement at the start of your method. This nullcheck will throw a `NullPointerException` with the name of the parameter as the message. [Issue #514](https://code.google.com/p/projectlombok/issues/detail?id=514)
* BUGFIX: Usage of `Lombok.sneakyThrow()` or `@SneakyThrows` would sometimes result in invalid classes (classes which fail with `VerifyError`). [Issue #470](https://code.google.com/p/projectlombok/issues/detail?id=470)
* BUGFIX: Using `val` in try-with-resources did not work for javac. [Issue #520](https://code.google.com/p/projectlombok/issues/detail?id=520)
* BUGFIX: When using `@Data`, warnings are not generated if certain aspects are not generated because you wrote explicit versions of them. However, this gets confusing with `equals` / `hashCode` / `canEqual`, as nothing is generated if any one of those methods is present. Now, if one of `equals` or `hashCode` is present but not the other one (or `canEqual` is present but `equals` and/or `hashCode` is missing), a warning is emitted to explain that lombok will not generate any of the equals / hashCode methods, and that you should either write them all yourself or remove them all. [Issue #513](https://code.google.com/p/projectlombok/issues/detail?id=513)
* BUGFIX: Possibly fixed a race condition in patcher [Issue #531](https://code.google.com/p/projectlombok/issues/detail?id=531).
### v0.11.8 (April 23rd, 2013)
* FEATURE: Major performance improvements in eclipse by profiling the project clean process.
* CHANGE: {Experimental} The experimental `@Value` feature no longer implies the also experimental `@Wither`. If you like your `@Value` classes to make withers, add `@Wither` to the class right next to `@Value`.
* FEATURE: {Experimental} Reintroduced `onMethod`, `onConstructor` and `onParam` to `@Getter`, `@Setter`, `@Wither`, and `@XArgsConstructor`. These parameters allow you to add annotations to the methods/constructors that lombok will generate. This is a workaround feature: The stability of the feature on future versions of javac is not guaranteed, and if a better way to implement this feature is found, this feature's current incarnation will be removed without a reasonable period of deprecation. [Documentation on the onX feature](http://projectlombok.org/features/experimental/onX.html)
* FEATURE: Added support for Log4j v2.0 via `@Log4j2` [Issue #432](http://code.google.com/p/projectlombok/issues/detail?id=432)
* ENHANCEMENT: The Lombok installer can now find and install lombok into [JBoss Developer Studio](http://www.redhat.com/products/jbossenterprisemiddleware/developer-studio/). The installer will now also look for eclipse and eclipse variants in your home directory. [Issue #434](http://code.google.com/p/projectlombok/issues/detail?id=432)
* BUGFIX: `@ExtensionMethods` no longer causes `VerifyError` exceptions when running eclipse-compiled code if extension methods are called on expressions which are method calls whose return type is a type variable. For example, `someList.get(i).extensionMethod()` would fail that way. [Issue #436](http://code.google.com/p/projectlombok/issues/detail?id=436)
* BUGFIX: java 7's try-with-resources statement did not delombok correctly. [Issue #459](http://code.google.com/p/projectlombok/issues/detail?id=459)
### v0.11.6 (October 30th, 2012)
* FEATURE: Lombok can be disabled entirely for any given compile run by using JVM switch `-Dlombok.disable`. This might be useful for code style checkers and such.
+2 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2012 The Project Lombok Authors.
* Copyright (C) 2009-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -39,7 +39,7 @@ import java.lang.annotation.Target;
* @see RequiredArgsConstructor
* @see ToString
* @see EqualsAndHashCode
* @see lombok.experimental.Value
* @see lombok.Value
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
+9 -7
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009 The Project Lombok Authors.
* Copyright (C) 2009-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -28,12 +28,14 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Lombok is smart enough to translate any annotation named {@code @NonNull} in any casing and
* with any package name to the return type of generated getters and the parameter of generated setters and constructors,
* as well as generate the appropriate null checks in the setter and constructor.
*
* You can use this annotation for the purpose, though you can also use JSR305's annotation, findbugs's, pmd's, or IDEA's, or just
* about anyone elses. As long as it is named {@code @NonNull}.
* If put on a parameter, lombok will insert a null-check at the start of the method / constructor's body, throwing a
* {@code NullPointerException} with the parameter's name as message. If put on a field, any generated method assigning
* a value to this field will also produce these nullchecks.
* <p>
* Note that any annotation named {@code NonNull} with any casing and any package will result in nullchecks produced for
* generated methods (and the annotation will be copied to the getter return type and any parameters of generated methods),
* but <em>only</em> this annotation, if present on a parameter, will result in a null check inserted into your otherwise
* handwritten method.
*
* WARNING: If the java community ever does decide on supporting a single {@code @NonNull} annotation (for example via JSR305), then
* this annotation will <strong>be deleted</strong> from the lombok package. If the need to update an import statement scares
+58
View File
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2012-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Generates a lot of code which fits with a class that is a representation of an immutable entity.
* <p>
* Equivalent to {@code @Getter @FieldDefaults(makeFinal=true, level=AccessLevel.PRIVATE) @RequiredArgsConstructor @ToString @EqualsAndHashCode}.
* <p>
* Complete documentation is found at <a href="http://projectlombok.org/features/experimental/Value.html">the project lombok features page for &#64;Value</a>.
*
* @see lombok.Getter
* @see lombok.experimental.FieldDefaults
* @see lombok.RequiredArgsConstructor
* @see lombok.ToString
* @see lombok.EqualsAndHashCode
* @see lombok.Data
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Value {
/**
* If you specify a static constructor name, then the generated constructor will be private, and
* instead a static factory method is created that other classes can use to create instances.
* We suggest the name: "of", like so:
*
* <pre>
* public @Data(staticConstructor = "of") class Point { final int x, y; }
* </pre>
*
* Default: No static constructor, instead the normal constructor is public.
*/
String staticConstructor() default "";
}
@@ -44,12 +44,12 @@ public class PreventNullAnalysisRemover implements PostCompilerTransformation {
byte[] fixedByteCode = fixJSRInlining(original);
ClassReader reader = new ClassReader(fixedByteCode);
ClassWriter writer = new FixedClassWriter(reader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
ClassWriter writer = new FixedClassWriter(reader, 0);
final AtomicBoolean changesMade = new AtomicBoolean();
class PreventNullanalysisVisitor extends MethodVisitor {
PreventNullanalysisVisitor(MethodVisitor mv) {
class PreventNullAnalysisVisitor extends MethodVisitor {
PreventNullAnalysisVisitor(MethodVisitor mv) {
super(Opcodes.ASM4, mv);
}
@@ -61,6 +61,7 @@ public class PreventNullAnalysisRemover implements PostCompilerTransformation {
if (hit && !"(Ljava/lang/Object;)Ljava/lang/Object;".equals(desc)) hit = false;
if (hit) {
changesMade.set(true);
if (System.getProperty("lombok.debugAsmOnly", null) != null) super.visitMethodInsn(opcode, owner, name, desc); // DEBUG for issue 470!
} else {
super.visitMethodInsn(opcode, owner, name, desc);
}
@@ -69,7 +70,7 @@ public class PreventNullAnalysisRemover implements PostCompilerTransformation {
reader.accept(new ClassVisitor(Opcodes.ASM4, writer) {
@Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
return new PreventNullanalysisVisitor(super.visitMethod(access, name, desc, signature, exceptions));
return new PreventNullAnalysisVisitor(super.visitMethod(access, name, desc, signature, exceptions));
}
}, 0);
return changesMade.get() ? writer.toByteArray() : null;
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2012 The Project Lombok Authors.
* Copyright (C) 2010-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -32,19 +32,21 @@ import org.mangosdk.spi.ProviderFor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@ProviderFor(PostCompilerTransformation.class)
public class SneakyThrowsRemover implements PostCompilerTransformation {
@Override public byte[] applyTransformations(byte[] original, String fileName, DiagnosticsReceiver diagnostics) {
@Override public byte[] applyTransformations(byte[] original, String fileName, final DiagnosticsReceiver diagnostics) {
if (!new ClassFileMetaData(original).usesMethod("lombok/Lombok", "sneakyThrow")) return null;
byte[] fixedByteCode = fixJSRInlining(original);
ClassReader reader = new ClassReader(fixedByteCode);
ClassWriter writer = new FixedClassWriter(reader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
ClassWriter writer = new ClassWriter(reader, 0);
final AtomicBoolean changesMade = new AtomicBoolean();
@@ -53,6 +55,8 @@ public class SneakyThrowsRemover implements PostCompilerTransformation {
super(Opcodes.ASM4, mv);
}
private boolean methodInsnQueued = false;
@Override public void visitMethodInsn(int opcode, String owner, String name, String desc) {
if (
opcode == Opcodes.INVOKESTATIC &&
@@ -60,12 +64,117 @@ public class SneakyThrowsRemover implements PostCompilerTransformation {
"lombok/Lombok".equals(owner) &&
"(Ljava/lang/Throwable;)Ljava/lang/RuntimeException;".equals(desc)) {
changesMade.set(true);
super.visitInsn(Opcodes.ATHROW);
if (System.getProperty("lombok.debugAsmOnly", null) != null) {
super.visitMethodInsn(opcode, owner, name, desc); // DEBUG for issue 470!
} else {
methodInsnQueued = true;
}
} else {
super.visitMethodInsn(opcode, owner, name, desc);
}
}
private void handleQueue() {
if (!methodInsnQueued) return;
super.visitMethodInsn(Opcodes.INVOKESTATIC, "lombok/Lombok", "sneakyThrow", "(Ljava/lang/Throwable;)Ljava/lang/RuntimeException;");
methodInsnQueued = false;
diagnostics.addWarning("Proper usage is: throw lombok.Lombok.sneakyThrow(someException);. You did not 'throw' it. Because of this, the call to sneakyThrow " +
"remains in your classfile and you will need lombok.jar on the classpath at runtime.");
}
@Override public void visitInsn(int arg0) {
if (methodInsnQueued && arg0 == Opcodes.ATHROW) {
changesMade.set(true);
// As expected, the required ATHROW. We can now safely 'eat' the previous call.
methodInsnQueued = false;
}
handleQueue();
super.visitInsn(arg0);
}
@Override public void visitFrame(int arg0, int arg1, Object[] arg2, int arg3, Object[] arg4) {
handleQueue();
super.visitFrame(arg0, arg1, arg2, arg3, arg4);
}
@Override public void visitIincInsn(int arg0, int arg1) {
handleQueue();
super.visitIincInsn(arg0, arg1);
}
@Override public void visitFieldInsn(int arg0, String arg1, String arg2, String arg3) {
handleQueue();
super.visitFieldInsn(arg0, arg1, arg2, arg3);
}
@Override public void visitIntInsn(int arg0, int arg1) {
handleQueue();
super.visitIntInsn(arg0, arg1);
}
@Override public void visitEnd() {
handleQueue();
super.visitEnd();
}
@Override public void visitInvokeDynamicInsn(String arg0, String arg1, Handle arg2, Object... arg3) {
handleQueue();
super.visitInvokeDynamicInsn(arg0, arg1, arg2, arg3);
}
@Override public void visitLabel(Label arg0) {
handleQueue();
super.visitLabel(arg0);
}
@Override public void visitJumpInsn(int arg0, Label arg1) {
handleQueue();
super.visitJumpInsn(arg0, arg1);
}
@Override public void visitLdcInsn(Object arg0) {
handleQueue();
super.visitLdcInsn(arg0);
}
@Override public void visitLocalVariable(String arg0, String arg1, String arg2, Label arg3, Label arg4, int arg5) {
handleQueue();
super.visitLocalVariable(arg0, arg1, arg2, arg3, arg4, arg5);
}
@Override public void visitMaxs(int arg0, int arg1) {
handleQueue();
super.visitMaxs(arg0, arg1);
}
@Override public void visitLookupSwitchInsn(Label arg0, int[] arg1, Label[] arg2) {
handleQueue();
super.visitLookupSwitchInsn(arg0, arg1, arg2);
}
@Override public void visitMultiANewArrayInsn(String arg0, int arg1) {
handleQueue();
super.visitMultiANewArrayInsn(arg0, arg1);
}
@Override public void visitVarInsn(int arg0, int arg1) {
handleQueue();
super.visitVarInsn(arg0, arg1);
}
@Override public void visitTryCatchBlock(Label arg0, Label arg1, Label arg2, String arg3) {
handleQueue();
super.visitTryCatchBlock(arg0, arg1, arg2, arg3);
}
@Override public void visitTableSwitchInsn(int arg0, int arg1, Label arg2, Label... arg3) {
handleQueue();
super.visitTableSwitchInsn(arg0, arg1, arg2, arg3);
}
@Override public void visitTypeInsn(int arg0, String arg1) {
handleQueue();
super.visitTypeInsn(arg0, arg1);
}
}
reader.accept(new ClassVisitor(Opcodes.ASM4, writer) {
+41 -20
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009 The Project Lombok Authors.
* Copyright (C) 2009-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -30,7 +30,6 @@ import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
@@ -54,15 +53,15 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
private L top;
private final String fileName;
private final String packageDeclaration;
private final Collection<String> imports;
Map<N, Void> identityDetector = new IdentityHashMap<N, Void>();
private final ImportList imports;
Map<N, N> identityDetector = new IdentityHashMap<N, N>();
private Map<N, L> nodeMap = new IdentityHashMap<N, L>();
private boolean changed = false;
protected AST(String fileName, String packageDeclaration, Collection<String> imports) {
protected AST(String fileName, String packageDeclaration, ImportList imports) {
this.fileName = fileName == null ? "(unknown).java" : fileName;
this.packageDeclaration = packageDeclaration;
this.imports = Collections.unmodifiableCollection(new ArrayList<String>(imports));
this.imports = imports;
}
public void setChanged() {
@@ -96,7 +95,7 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
*
* Example: "java.util.IOException".
*/
public final Collection<String> getImportStatements() {
public final ImportList getImportList() {
return imports;
}
@@ -106,7 +105,7 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
*/
protected L putInMap(L node) {
nodeMap.put(node.get(), node);
identityDetector.put(node.get(), null);
identityDetector.put(node.get(), node.get());
return node;
}
@@ -118,7 +117,7 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
/** Clears the registry that avoids endless loops, and empties the node map. The existing node map
* object is left untouched, and instead a new map is created. */
protected void clearState() {
identityDetector = new IdentityHashMap<N, Void>();
identityDetector = new IdentityHashMap<N, N>();
nodeMap = new IdentityHashMap<N, L>();
}
@@ -128,9 +127,7 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
* case you should do nothing lest the AST build process loops endlessly.
*/
protected boolean setAndGetAsHandled(N node) {
if (identityDetector.containsKey(node)) return true;
identityDetector.put(node, null);
return false;
return identityDetector.put(node, node) != null;
}
public String getFileName() {
@@ -147,6 +144,24 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
return nodeMap.get(node);
}
/**
* Returns the JLS spec version that the compiler uses to parse and compile this AST.
* For example, if -source 1.6 is on the command line, this will return {@code 6}.
*/
public int getSourceVersion() {
return 6;
}
/**
* Returns the latest version of the java language specification supported by the host compiler.
* For example, if compiling with javac v1.7, this returns {@code 7}.
*
* NB: Even if -source (lower than maximum) is specified, this method still returns the maximum supported number.
*/
public int getLatestJavaSpecSupported() {
return 6;
}
@SuppressWarnings({"unchecked", "rawtypes"})
L replaceNewWithExistingOld(Map<N, L> oldNodes, L newNode) {
L oldNode = oldNodes.get(newNode.get());
@@ -159,8 +174,8 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
oldChild.parent = targetNode;
}
targetNode.children.clear();
((List)targetNode.children).addAll(children);
targetNode.children = LombokImmutableList.copyOf(children);
return targetNode;
}
@@ -217,14 +232,12 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
}
}
for (Class<?> statementType : getStatementTypes()) {
if (statementType.isAssignableFrom(t)) {
f.setAccessible(true);
fields.add(new FieldAccess(f, dim));
break;
}
if (shouldDrill(c, t, f.getName())) {
f.setAccessible(true);
fields.add(new FieldAccess(f, dim));
}
}
getFields(c.getSuperclass(), fields);
}
@@ -241,6 +254,14 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
* though some platforms (such as Eclipse) group these under one common supertype. */
protected abstract Collection<Class<? extends N>> getStatementTypes();
protected boolean shouldDrill(Class<?> parentType, Class<?> childType, String fieldName) {
for (Class<?> statementType : getStatementTypes()) {
if (statementType.isAssignableFrom(childType)) return true;
}
return false;
}
/**
* buildTree implementation that uses reflection to find all child nodes by way of inspecting
* the fields. */
@@ -164,12 +164,9 @@ public class AnnotationProcessor extends AbstractProcessor {
}
}
boolean handled = false;
for (ProcessorDescriptor proc : active) {
handled |= proc.process(annotations, roundEnv);
}
for (ProcessorDescriptor proc : active) proc.process(annotations, roundEnv);
return handled;
return false;
}
/**
+6 -13
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2012 The Project Lombok Authors.
* Copyright (C) 2009-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -435,23 +435,16 @@ public class AnnotationValues<A extends Annotation> {
}
/* 2. Walk through non-star imports and search for a match. */ {
for (String im : ast == null ? Collections.<String>emptyList() : ast.getImportStatements()) {
if (im.endsWith(".*")) continue;
int idx = im.lastIndexOf('.');
String simple = idx == -1 ? im : im.substring(idx+1);
if (simple.equals(prefix)) {
return im + typeName.substring(prefix.length());
}
if (prefix.equals(typeName)) {
String fqn = ast.getImportList().getFullyQualifiedNameForSimpleName(typeName);
if (fqn != null) return fqn;
}
}
/* 3. Walk through star imports and, if they start with "java.", use Class.forName based resolution. */ {
List<String> imports = ast == null ? Collections.<String>emptyList() : new ArrayList<String>(ast.getImportStatements());
imports.add("java.lang.*");
for (String im : imports) {
if (!im.endsWith(".*") || !im.startsWith("java.")) continue;
for (String potential : ast.getImportList().applyNameToStarImports("java", typeName)) {
try {
Class<?> c = Class.forName(im.substring(0, im.length()-1) + typeName);
Class<?> c = Class.forName(potential);
if (c != null) return c.getName();
} catch (Throwable t) {
//Class.forName failed for whatever reason - it most likely does not exist, continue.
+43
View File
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.core;
import java.util.Collection;
public interface ImportList {
/**
* If there is an explicit import of the stated unqualified type name, return that. Otherwise, return null.
*/
String getFullyQualifiedNameForSimpleName(String unqualified);
/**
* Returns true if the package name is explicitly star-imported, OR the packageName refers to this source file's own package name, OR packageName is 'java.lang'.
*/
boolean hasStarImport(String packageName);
/**
* Takes all explicit non-static star imports whose first element is equal to {@code startsWith}, replaces the star with {@code unqualified}, and returns these.
*/
Collection<String> applyNameToStarImports(String startsWith, String unqualified);
String applyUnqualifiedNameToPackage(String unqualified);
}
@@ -0,0 +1,52 @@
/*
* Copyright (C) 2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.core;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class LombokInternalAliasing {
public static final Map<String, String> IMPLIED_EXTRA_STAR_IMPORTS;
public static final Map<String, String> ALIASES;
/**
* Provide a fully qualified name (FQN), and the canonical version of this is returned.
*/
public static String processAliases(String in) {
if (in == null) return null;
for (Map.Entry<String, String> e : ALIASES.entrySet()) {
if (in.equals(e.getKey())) return e.getValue();
}
return in;
}
static {
Map<String, String> m = new HashMap<String, String>();
m.put("lombok.experimental", "lombok");
IMPLIED_EXTRA_STAR_IMPORTS = Collections.unmodifiableMap(m);
m = new HashMap<String, String>();
m.put("lombok.experimental.Value", "lombok.Value");
ALIASES = Collections.unmodifiableMap(m);
}
}
+31 -45
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2011 The Project Lombok Authors.
* Copyright (C) 2009-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -42,7 +42,7 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A,
protected final A ast;
protected final Kind kind;
protected final N node;
protected final List<L> children;
protected LombokImmutableList<L> children;
protected L parent;
/** structurally significant are those nodes that can be annotated in java 1.6 or are method-like toplevels,
@@ -62,7 +62,7 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A,
this.ast = ast;
this.kind = kind;
this.node = node;
this.children = children == null ? new ArrayList<L>() : children;
this.children = children != null ? LombokImmutableList.copyOf(children) : LombokImmutableList.<L>of();
for (L child : this.children) {
child.parent = (L) this;
if (!child.isStructurallySignificant)
@@ -91,12 +91,12 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A,
}
/**
* Convenient shortcut to the owning ast object's {@code getImportStatements} method.
* Convenient shortcut to the owning ast object's {@code getImportList} method.
*
* @see AST#getImportStatements()
* @see AST#getImportList()
*/
public Collection<String> getImportStatements() {
return ast.getImportStatements();
public ImportList getImportList() {
return ast.getImportList();
}
/**
@@ -120,35 +120,6 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A,
return node;
}
/**
* Replaces the AST node represented by this node object with the provided node. This node must
* have a parent, obviously, for this to work.
*
* Also affects the underlying (Eclipse/javac) AST.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public L replaceWith(N newN, Kind newNodeKind) {
ast.setChanged();
L newNode = ast.buildTree(newN, newNodeKind);
newNode.parent = parent;
for (int i = 0; i < parent.children.size(); i++) {
if (parent.children.get(i) == this) ((List)parent.children).set(i, newNode);
}
parent.replaceChildNode(get(), newN);
return newNode;
}
/**
* Replaces the stated node with a new one. The old node must be a direct child of this node.
*
* Also affects the underlying (Eclipse/javac) AST.
*/
public void replaceChildNode(N oldN, N newN) {
ast.setChanged();
ast.replaceStatementInNode(get(), oldN, newN);
}
public Kind getKind() {
return kind;
}
@@ -204,12 +175,27 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A,
/**
* Returns all children nodes.
*
* A copy is created, so changing the list has no effect. Also, while iterating through this list,
* you may add, remove, or replace children without causing {@code ConcurrentModificationException}s.
*/
public Collection<L> down() {
return new ArrayList<L>(children);
public LombokImmutableList<L> down() {
return children;
}
/**
* Convenient shortcut to the owning ast object's getLatestJavaSpecSupported method.
*
* @see AST#getLatestJavaSpecSupported()
*/
public int getLatestJavaSpecSupported() {
return ast.getLatestJavaSpecSupported();
}
/**
* Convenient shortcut to the owning ast object's getSourceVersion method.
*
* @see AST#getSourceVersion()
*/
public int getSourceVersion() {
return ast.getSourceVersion();
}
/**
@@ -235,13 +221,13 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A,
*
* Does not change the underlying (javac/Eclipse) AST, only the wrapped view.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
@SuppressWarnings({"unchecked"})
public L add(N newChild, Kind newChildKind) {
ast.setChanged();
L n = ast.buildTree(newChild, newChildKind);
if (n == null) return null;
n.parent = (L) this;
((List)children).add(n);
children = children.append(n);
return n;
}
@@ -267,7 +253,7 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A,
for (LombokNode child : children) child.gatherAndRemoveChildren(map);
ast.identityDetector.remove(get());
map.put(get(), (L) this);
children.clear();
children = LombokImmutableList.of();
ast.getNodeMap().remove(get());
}
@@ -278,7 +264,7 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A,
*/
public void removeChild(L child) {
ast.setChanged();
children.remove(child);
children = children.removeElement(child);
}
/**
+3 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010 The Project Lombok Authors.
* Copyright (C) 2010-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -33,6 +33,7 @@ public final class PostCompiler {
private static List<PostCompilerTransformation> transformations;
public static byte[] applyTransformations(byte[] original, String fileName, DiagnosticsReceiver diagnostics) {
if (System.getProperty("lombok.disablePostCompiler", null) != null) return original;
init(diagnostics);
byte[] previous = original;
for (PostCompilerTransformation transformation : transformations) {
@@ -59,6 +60,7 @@ public final class PostCompiler {
}
public static OutputStream wrapOutputStream(final OutputStream originalStream, final String fileName, final DiagnosticsReceiver diagnostics) throws IOException {
if (System.getProperty("lombok.disablePostCompiler", null) != null) return originalStream;
return new ByteArrayOutputStream() {
@Override public void close() throws IOException {
// no need to call super
+22 -3
View File
@@ -22,13 +22,25 @@
package lombok.core;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.Value;
import lombok.experimental.Accessors;
import lombok.experimental.FieldDefaults;
import lombok.experimental.Wither;
/**
* Container for static utility methods useful for some of the standard lombok transformations, regardless of
@@ -39,6 +51,13 @@ public class TransformationsUtil {
//Prevent instantiation
}
@SuppressWarnings({"all", "unchecked", "deprecation"})
public static final List<Class<? extends java.lang.annotation.Annotation>> INVALID_ON_BUILDERS = Collections.unmodifiableList(
Arrays.<Class<? extends java.lang.annotation.Annotation>>asList(
Getter.class, Setter.class, Wither.class, ToString.class, EqualsAndHashCode.class,
RequiredArgsConstructor.class, AllArgsConstructor.class, NoArgsConstructor.class,
Data.class, Value.class, lombok.experimental.Value.class, FieldDefaults.class));
/**
* Given the name of a field, return the 'base name' of that field. For example, {@code fFoobar} becomes {@code foobar} if {@code f} is in the prefix list.
* For prefixes that end in a letter character, the next character must be a non-lowercase character (i.e. {@code hashCode} is not {@code ashCode} even if
@@ -159,12 +178,12 @@ public class TransformationsUtil {
if (fieldName.length() == 0) return null;
Accessors ac = accessors.getInstance();
fieldName = removePrefix(fieldName, ac.prefix());
Accessors ac = accessors == null ? null : accessors.getInstance();
fieldName = removePrefix(fieldName, ac == null ? new String[0] : ac.prefix());
if (fieldName == null) return null;
String fName = fieldName.toString();
if (adhereToFluent && ac.fluent()) return fName;
if (adhereToFluent && ac != null && ac.fluent()) return fName;
if (isBoolean && fName.startsWith("is") && fieldName.length() > 2 && !Character.isLowerCase(fieldName.charAt(2))) {
// The field is for example named 'isRunning'.
+29 -43
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2012 The Project Lombok Authors.
* Copyright (C) 2009-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,12 +21,7 @@
*/
package lombok.core;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@@ -40,24 +35,23 @@ import java.util.Map;
* <ul><li>foo.Spork</li><li>Spork</li><li>foo.*</li></ul>
*/
public class TypeLibrary {
private final Map<String, List<String>> keyToFqnMap;
private final String singletonValue;
private final List<String> singletonKeys;
private final Map<String, String> unqualifiedToQualifiedMap;
private final String unqualified, qualified;
public TypeLibrary() {
keyToFqnMap = new HashMap<String, List<String>>();
singletonKeys = null;
singletonValue = null;
unqualifiedToQualifiedMap = new HashMap<String, String>();
unqualified = null;
qualified = null;
}
private TypeLibrary(String fqnSingleton) {
keyToFqnMap = null;
singletonValue = fqnSingleton;
unqualifiedToQualifiedMap = null;
qualified = fqnSingleton;
int idx = fqnSingleton.lastIndexOf('.');
if (idx == -1) {
singletonKeys = Collections.singletonList(fqnSingleton);
unqualified = fqnSingleton;
} else {
singletonKeys = Arrays.asList(fqnSingleton, fqnSingleton.substring(idx + 1), fqnSingleton.substring(0, idx) + ".*");
unqualified = fqnSingleton.substring(idx + 1);
}
}
@@ -71,42 +65,34 @@ public class TypeLibrary {
* @param fullyQualifiedTypeName the FQN type name, such as 'java.lang.String'.
*/
public void addType(String fullyQualifiedTypeName) {
if (keyToFqnMap == null) throw new IllegalStateException("SingleType library");
fullyQualifiedTypeName = fullyQualifiedTypeName.replace("$", ".");
int idx = fullyQualifiedTypeName.lastIndexOf('.');
if (idx == -1) throw new IllegalArgumentException(
"Only fully qualified types are allowed (and stuff in the default package is not palatable to us either!)");
String unqualified = fullyQualifiedTypeName.substring(idx + 1);
if (unqualifiedToQualifiedMap == null) throw new IllegalStateException("SingleType library");
fullyQualifiedTypeName = fullyQualifiedTypeName.replace("$", ".");
final String simpleName = fullyQualifiedTypeName.substring(idx +1);
final String packageName = fullyQualifiedTypeName.substring(0, idx);
if (keyToFqnMap.put(fullyQualifiedTypeName, Collections.singletonList(fullyQualifiedTypeName)) != null) return;
addToMap(simpleName, fullyQualifiedTypeName);
addToMap(packageName + ".*", fullyQualifiedTypeName);
}
private TypeLibrary addToMap(String keyName, String fullyQualifiedTypeName) {
List<String> list = keyToFqnMap.get(keyName);
if (list == null) {
list = new ArrayList<String>();
keyToFqnMap.put(keyName, list);
unqualifiedToQualifiedMap.put(unqualified, fullyQualifiedTypeName);
unqualifiedToQualifiedMap.put(fullyQualifiedTypeName, fullyQualifiedTypeName);
for (Map.Entry<String, String> e : LombokInternalAliasing.ALIASES.entrySet()) {
if (fullyQualifiedTypeName.equals(e.getValue())) unqualifiedToQualifiedMap.put(e.getKey(), fullyQualifiedTypeName);
}
list.add(fullyQualifiedTypeName);
return this;
}
/**
* Returns all items in the type library that may be a match to the provided type.
* Translates an unqualified name such as 'String' to 'java.lang.String', _if_ you added 'java.lang.String' to the library via the {@code addType} method.
* Also returns the input if it is equal to a fully qualified name added to this type library.
*
* @param typeReference something like 'String', 'java.lang.String', or 'java.lang.*'.
* @return A list of Fully Qualified Names for all types in the library that fit the reference.
* Returns null if it does not match any type in this type library.
*/
public Collection<String> findCompatible(String typeReference) {
if (singletonKeys != null) return singletonKeys.contains(typeReference) ? Collections.singletonList(singletonValue) : Collections.<String>emptyList();
List<String> result = keyToFqnMap.get(typeReference);
return result == null ? Collections.<String>emptyList() : Collections.unmodifiableList(result);
public String toQualified(String typeReference) {
if (unqualifiedToQualifiedMap == null) {
if (typeReference.equals(unqualified) || typeReference.equals(qualified)) return qualified;
for (Map.Entry<String, String> e : LombokInternalAliasing.ALIASES.entrySet()) {
if (e.getKey().equals(typeReference)) return e.getValue();
}
return null;
}
return unqualifiedToQualifiedMap.get(typeReference);
}
}
+25 -70
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2011 The Project Lombok Authors.
* Copyright (C) 2009-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,11 +21,6 @@
*/
package lombok.core;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import lombok.core.AST.Kind;
/**
@@ -35,60 +30,47 @@ import lombok.core.AST.Kind;
* and this importer also can't find inner types from superclasses/interfaces.
*/
public class TypeResolver {
private Collection<String> imports;
private ImportList imports;
/**
* Creates a new TypeResolver that can be used to resolve types in a source file with the given package and import statements.
*/
public TypeResolver(String packageString, Collection<String> importStrings) {
this.imports = makeImportList(packageString, importStrings);
}
private static Collection<String> makeImportList(String packageString, Collection<String> importStrings) {
Set<String> imports = new HashSet<String>();
if (packageString != null) imports.add(packageString + ".*");
imports.addAll(importStrings == null ? Collections.<String>emptySet() : importStrings);
imports.add("java.lang.*");
return imports;
public TypeResolver(ImportList importList) {
this.imports = importList;
}
public boolean typeMatches(LombokNode<?, ?, ?> context, String fqn, String typeRef) {
return !findTypeMatches(context, TypeLibrary.createLibraryForSingleType(fqn), typeRef).isEmpty();
return typeRefToFullyQualifiedName(context, TypeLibrary.createLibraryForSingleType(fqn), typeRef) != null;
}
/**
* Finds type matches for the stated type reference. The provided context is scanned for local type names
* that shadow type names listed in import statements. If such a shadowing occurs, no matches are returned
* for any shadowed types, as you would expect.
*/
public Collection<String> findTypeMatches(LombokNode<?, ?, ?> context, TypeLibrary library, String typeRef) {
public String typeRefToFullyQualifiedName(LombokNode<?, ?, ?> context, TypeLibrary library, String typeRef) {
typeRef = LombokInternalAliasing.processAliases(typeRef);
// When asking if 'Foo' could possibly be referring to 'bar.Baz', the answer is obviously no.
Collection<String> potentialMatches = library.findCompatible(typeRef);
if (potentialMatches.isEmpty()) return Collections.emptyList();
String qualified = library.toQualified(typeRef);
if (qualified == null) return null;
// If input type appears to be fully qualified, we found a winner.
int idx = typeRef.indexOf('.');
if (idx > -1) return potentialMatches;
// When asking if 'lombok.Getter' could possibly be referring to 'lombok.Getter', the answer is obviously yes.
if (typeRef.equals(qualified)) return typeRef;
// If there's an import statement that explicitly imports a 'Getter' that isn't any of our potentials, return no matches,
// because if you want to know if 'Foo' could refer to 'bar.Foo' when 'baz.Foo' is explicitly imported, the answer is no.
if (nameConflictInImportList(typeRef, potentialMatches)) return Collections.emptyList();
// When asking if 'Getter' could possibly be referring to 'lombok.Getter' if 'import lombok.Getter;' is in the source file, the answer is yes.
String fromExplicitImport = imports.getFullyQualifiedNameForSimpleName(typeRef);
if (fromExplicitImport != null) {
// ... and if 'import foobar.Getter;' is in the source file, the answer is no.
return fromExplicitImport.equals(qualified) ? qualified : null;
}
// Check if any of our potentials are even imported in the first place. If not: no matches.
// Note that (ourPackage.*) is added to the imports.
potentialMatches = eliminateImpossibleMatches(potentialMatches, library);
if (potentialMatches.isEmpty()) return Collections.emptyList();
// When asking if 'Getter' could possibly be referring to 'lombok.Getter' and 'import lombok.*; / package lombok;' isn't in the source file. the answer is no.
String pkgName = qualified.substring(0, qualified.length() - typeRef.length() - 1);
if (!imports.hasStarImport(pkgName)) return null;
// Now the hard part - inner classes or method local classes in our own scope.
// For method locals, this refers to any statements that are 'above' the type reference with the same name.
// For inners, this refers to siblings of us or any parent node that are type declarations.
// Now the hard part: Given that there is a star import, 'Getter' most likely refers to 'lombok.Getter', but type shadowing may occur in which case it doesn't.
LombokNode<?, ?, ?> n = context;
mainLoop:
while (n != null) {
if (n.getKind() == Kind.TYPE && typeRef.equals(n.getName())) {
// Our own class or one of our outer classes is named 'typeRef' so that's what 'typeRef' is referring to, not one of our type library classes.
return Collections.emptyList();
return null;
}
if (n.getKind() == Kind.STATEMENT || n.getKind() == Kind.LOCAL) {
@@ -99,7 +81,7 @@ public class TypeResolver {
for (LombokNode<?, ?, ?> child : newN.down()) {
// We found a method local with the same name above our code. That's the one 'typeRef' is referring to, not
// anything in the type library we're trying to find, so, no matches.
if (child.getKind() == Kind.TYPE && typeRef.equals(child.getName())) return Collections.emptyList();
if (child.getKind() == Kind.TYPE && typeRef.equals(child.getName())) return null;
if (child == n) break;
}
}
@@ -110,41 +92,14 @@ public class TypeResolver {
if (n.getKind() == Kind.TYPE || n.getKind() == Kind.COMPILATION_UNIT) {
for (LombokNode<?, ?, ?> child : n.down()) {
// Inner class that's visible to us has 'typeRef' as name, so that's the one being referred to, not one of our type library classes.
if (child.getKind() == Kind.TYPE && typeRef.equals(child.getName())) return Collections.emptyList();
if (child.getKind() == Kind.TYPE && typeRef.equals(child.getName())) return null;
}
}
n = n.directUp();
}
// No class in this source file is a match, therefore the potential matches found via the import statements must be it. Return those.
return potentialMatches;
}
private Collection<String> eliminateImpossibleMatches(Collection<String> potentialMatches, TypeLibrary library) {
Set<String> results = new HashSet<String>();
for (String importedType : imports) {
Collection<String> reduced = new HashSet<String>(library.findCompatible(importedType));
reduced.retainAll(potentialMatches);
results.addAll(reduced);
}
return results;
}
private boolean nameConflictInImportList(String simpleName, Collection<String> potentialMatches) {
for (String importedType : imports) {
if (!toSimpleName(importedType).equals(simpleName)) continue;
if (potentialMatches.contains(importedType)) continue;
return true;
}
return false;
}
private static String toSimpleName(String typeName) {
int idx = typeName.lastIndexOf('.');
return idx == -1 ? typeName : typeName.substring(idx+1);
return qualified;
}
}
+2 -2
View File
@@ -28,8 +28,8 @@ public class Version {
// ** CAREFUL ** - this class must always compile with 0 dependencies (it must not refer to any other sources or libraries).
// Note: In 'X.Y.Z', if Z is odd, its a snapshot build built from the repository, so many different 0.10.3 versions can exist, for example.
// Official builds always end in an even number. (Since 0.10.2).
private static final String VERSION = "0.11.7";
private static final String RELEASE_NAME = "Dashing Kakapo";
private static final String VERSION = "0.12.1";
private static final String RELEASE_NAME = "Angry Butterfy";
private Version() {
//Prevent instantiation
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.core.handlers;
import lombok.core.JavaIdentifiers;
import lombok.core.LombokNode;
public class HandlerUtil {
private HandlerUtil() {}
/** Checks if the given name is a valid identifier.
*
* If it is, this returns {@code true} and does nothing else.
* If it isn't, this returns {@code false} and adds an error message to the supplied node.
*/
public static boolean checkName(String nameSpec, String identifier, LombokNode<?, ?, ?> errorNode) {
if (identifier.isEmpty()) {
errorNode.addError(nameSpec + " cannot be the empty string.");
return false;
}
if (!JavaIdentifiers.isValidJavaIdentifier(identifier)) {
errorNode.addError(nameSpec + " must be a valid java identifier.");
return false;
}
return true;
}
}
+27 -21
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2010 The Project Lombok Authors.
* Copyright (C) 2009-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,12 +24,14 @@ package lombok.eclipse;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import lombok.Lombok;
import lombok.core.AST;
import lombok.core.LombokImmutableList;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
@@ -55,7 +57,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
* @param ast The compilation unit, which serves as the top level node in the tree to be built.
*/
public EclipseAST(CompilationUnitDeclaration ast) {
super(toFileName(ast), packageDeclaration(ast), imports(ast));
super(toFileName(ast), packageDeclaration(ast), new EclipseImportList(ast));
this.compilationUnitDeclaration = ast;
setTop(buildCompilationUnit(ast));
this.completeParse = isComplete(ast);
@@ -67,16 +69,18 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
return pkg == null ? null : Eclipse.toQualifiedName(pkg.getImportName());
}
private static Collection<String> imports(CompilationUnitDeclaration cud) {
List<String> imports = new ArrayList<String>();
if (cud.imports == null) return imports;
for (ImportReference imp : cud.imports) {
if (imp == null) continue;
String qualifiedName = Eclipse.toQualifiedName(imp.getImportName());
if ((imp.bits & ASTNode.OnDemand) != 0) qualifiedName += ".*";
imports.add(qualifiedName);
}
return imports;
@Override public int getSourceVersion() {
long sl = compilationUnitDeclaration.problemReporter.options.sourceLevel;
long cl = compilationUnitDeclaration.problemReporter.options.complianceLevel;
sl >>= 16;
cl >>= 16;
if (sl == 0) sl = cl;
if (cl == 0) cl = sl;
return Math.min((int)(sl - 44), (int)(cl - 44));
}
@Override public int getLatestJavaSpecSupported() {
return Eclipse.getEcjCompilerVersion();
}
/**
@@ -88,8 +92,10 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
}
void traverseChildren(EclipseASTVisitor visitor, EclipseNode node) {
for (EclipseNode child : node.down()) {
child.traverse(visitor);
LombokImmutableList<EclipseNode> children = node.down();
int len = children.size();
for (int i = 0; i < len; i++) {
children.get(i).traverse(visitor);
}
}
@@ -118,7 +124,8 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
}
void addToCompilationResult() {
addProblemToCompilationResult((CompilationUnitDeclaration) top().get(),
CompilationUnitDeclaration cud = (CompilationUnitDeclaration) top().get();
addProblemToCompilationResult(cud.getFileName(), cud.compilationResult,
isWarning, message, sourceStart, sourceEnd);
}
}
@@ -142,11 +149,10 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
* Adds a problem to the provided CompilationResult object so that it will show up
* in the Problems/Warnings view.
*/
public static void addProblemToCompilationResult(CompilationUnitDeclaration ast,
public static void addProblemToCompilationResult(char[] fileNameArray, CompilationResult result,
boolean isWarning, String message, int sourceStart, int sourceEnd) {
if (ast.compilationResult == null) return;
try {
EcjReflectionCheck.addProblemToCompilationResult.invoke(null, ast, isWarning, message, sourceStart, sourceEnd);
EcjReflectionCheck.addProblemToCompilationResult.invoke(null, fileNameArray, result, isWarning, message, sourceStart, sourceEnd);
} catch (NoClassDefFoundError e) {
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
//do anything useful here.
@@ -163,7 +169,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
//do anything useful here.
}
}
private final CompilationUnitDeclaration compilationUnitDeclaration;
private boolean completeParse;
@@ -353,7 +359,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
}
private static class EcjReflectionCheck {
private static final String CUD_TYPE = "org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration";
private static final String COMPILATIONRESULT_TYPE = "org.eclipse.jdt.internal.compiler.CompilationResult";
public static Method addProblemToCompilationResult;
public static final Throwable problem;
@@ -362,7 +368,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
Throwable problem_ = null;
Method m = null;
try {
m = EclipseAstProblemView.class.getMethod("addProblemToCompilationResult", Class.forName(CUD_TYPE), boolean.class, String.class, int.class, int.class);
m = EclipseAstProblemView.class.getMethod("addProblemToCompilationResult", char[].class, Class.forName(COMPILATIONRESULT_TYPE), boolean.class, String.class, int.class, int.class);
} catch (Throwable t) {
// That's problematic, but as long as no local classes are used we don't actually need it.
// Better fail on local classes than crash altogether.
@@ -29,7 +29,7 @@ import lombok.core.SpiLoadUtil;
*
* You MUST replace 'T' with a specific annotation type, such as:
*
* {@code public class HandleGetter implements EclipseAnnotationHandler<Getter>}
* {@code public class HandleGetter extends EclipseAnnotationHandler<Getter>}
*
* Because this generics parameter is inspected to figure out which class you're interested in.
*
@@ -3,7 +3,6 @@ package lombok.eclipse;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.problem.DefaultProblem;
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
import org.eclipse.jdt.internal.compiler.util.Util;
@@ -13,14 +12,12 @@ public class EclipseAstProblemView {
* Adds a problem to the provided CompilationResult object so that it will show up
* in the Problems/Warnings view.
*/
public static void addProblemToCompilationResult(CompilationUnitDeclaration ast,
public static void addProblemToCompilationResult(char[] fileNameArray, CompilationResult result,
boolean isWarning, String message, int sourceStart, int sourceEnd) {
if (ast.compilationResult == null) return;
char[] fileNameArray = ast.getFileName();
if (result == null) return;
if (fileNameArray == null) fileNameArray = "(unknown).java".toCharArray();
int lineNumber = 0;
int columnNumber = 1;
CompilationResult result = ast.compilationResult;
int[] lineEnds = null;
lineNumber = sourceStart >= 0
? Util.getLineNumber(sourceStart, lineEnds = result.getLineSeparatorPositions(), 0, lineEnds.length-1)
@@ -33,7 +30,7 @@ public class EclipseAstProblemView {
fileNameArray, message, 0, new String[0],
isWarning ? ProblemSeverities.Warning : ProblemSeverities.Error,
sourceStart, sourceEnd, lineNumber, columnNumber);
ast.compilationResult.record(ecProblem, null);
result.record(ecProblem, null);
}
private static class LombokProblem extends DefaultProblem {
@@ -0,0 +1,136 @@
/*
* Copyright (C) 2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.eclipse;
import static lombok.eclipse.Eclipse.toQualifiedName;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import lombok.core.ImportList;
import lombok.core.LombokInternalAliasing;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
public class EclipseImportList implements ImportList {
private ImportReference[] imports;
private ImportReference pkg;
public EclipseImportList(CompilationUnitDeclaration cud) {
this.pkg = cud.currentPackage;
this.imports = cud.imports;
}
@Override public String getFullyQualifiedNameForSimpleName(String unqualified) {
if (imports != null) {
outer:
for (ImportReference imp : imports) {
if ((imp.bits & ASTNode.OnDemand) != 0) continue;
char[][] tokens = imp.tokens;
char[] token = tokens.length == 0 ? new char[0] : tokens[tokens.length - 1];
int len = token.length;
if (len != unqualified.length()) continue;
for (int i = 0; i < len; i++) if (token[i] != unqualified.charAt(i)) continue outer;
return LombokInternalAliasing.processAliases(toQualifiedName(tokens));
}
}
return null;
}
@Override public boolean hasStarImport(String packageName) {
for (Map.Entry<String, String> e : LombokInternalAliasing.IMPLIED_EXTRA_STAR_IMPORTS.entrySet()) {
if (e.getValue().equals(packageName) && hasStarImport(e.getKey())) return true;
}
if (isEqual(packageName, pkg)) return true;
if ("java.lang".equals(packageName)) return true;
if (imports != null) for (ImportReference imp : imports) {
if ((imp.bits & ASTNode.OnDemand) == 0) continue;
if (imp.isStatic()) continue;
if (isEqual(packageName, imp)) return true;
}
return false;
}
private static boolean isEqual(String packageName, ImportReference pkgOrStarImport) {
if (pkgOrStarImport == null || pkgOrStarImport.tokens == null || pkgOrStarImport.tokens.length == 0) return packageName.isEmpty();
int pos = 0;
int len = packageName.length();
for (int i = 0; i < pkgOrStarImport.tokens.length; i++) {
if (i != 0) {
if (pos >= len) return false;
if (packageName.charAt(pos++) != '.') return false;
}
for (int j = 0; j < pkgOrStarImport.tokens[i].length; j++) {
if (pos >= len) return false;
if (packageName.charAt(pos++) != pkgOrStarImport.tokens[i][j]) return false;
}
}
return true;
}
@Override public Collection<String> applyNameToStarImports(String startsWith, String name) {
List<String> out = Collections.emptyList();
if (pkg != null && pkg.tokens != null && pkg.tokens.length != 0) {
char[] first = pkg.tokens[0];
int len = first.length;
boolean match = true;
if (startsWith.length() == len) {
for (int i = 0; match && i < len; i++) {
if (startsWith.charAt(i) != first[i]) match = false;
}
if (match) out.add(toQualifiedName(pkg.tokens) + "." + name);
}
}
if (imports != null) {
outer:
for (ImportReference imp : imports) {
if ((imp.bits & ASTNode.OnDemand) == 0) continue;
if (imp.isStatic()) continue;
if (imp.tokens == null || imp.tokens.length == 0) continue;
char[] firstToken = imp.tokens[0];
if (firstToken.length != startsWith.length()) continue;
for (int i = 0; i < firstToken.length; i++) if (startsWith.charAt(i) != firstToken[i]) continue outer;
String fqn = toQualifiedName(imp.tokens) + "." + name;
if (out.isEmpty()) out = Collections.singletonList(fqn);
else if (out.size() == 1) {
out = new ArrayList<String>(out);
out.add(fqn);
} else {
out.add(fqn);
}
}
}
return out;
}
@Override public String applyUnqualifiedNameToPackage(String unqualified) {
if (pkg == null || pkg.tokens == null || pkg.tokens.length == 0) return unqualified;
return toQualifiedName(pkg.tokens) + "." + unqualified;
}
}
+19 -22
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2012 The Project Lombok Authors.
* Copyright (C) 2009-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -218,30 +218,27 @@ public class HandlerLibrary {
* @param annotation 'node.get()' - convenience parameter.
*/
public void handleAnnotation(CompilationUnitDeclaration ast, EclipseNode annotationNode, org.eclipse.jdt.internal.compiler.ast.Annotation annotation, long priority) {
String pkgName = annotationNode.getPackageDeclaration();
Collection<String> imports = annotationNode.getImportStatements();
TypeResolver resolver = new TypeResolver(pkgName, imports);
TypeResolver resolver = new TypeResolver(annotationNode.getImportList());
TypeReference rawType = annotation.type;
if (rawType == null) return;
for (String fqn : resolver.findTypeMatches(annotationNode, typeLibrary, toQualifiedName(annotation.type.getTypeName()))) {
AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn);
if (container == null) continue;
if (priority != container.getPriority()) continue;
if (!annotationNode.isCompleteParse() && container.deferUntilPostDiet()) {
if (needsHandling(annotation)) container.preHandle(annotation, annotationNode);
continue;
}
try {
if (checkAndSetHandled(annotation)) container.handle(annotation, annotationNode);
} catch (AnnotationValueDecodeFail fail) {
fail.owner.setError(fail.getMessage(), fail.idx);
} catch (Throwable t) {
error(ast, String.format("Lombok annotation handler %s failed", container.handler.getClass()), t);
}
String fqn = resolver.typeRefToFullyQualifiedName(annotationNode, typeLibrary, toQualifiedName(annotation.type.getTypeName()));
if (fqn == null) return;
AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn);
if (container == null) return;
if (priority != container.getPriority()) return;
if (!annotationNode.isCompleteParse() && container.deferUntilPostDiet()) {
if (needsHandling(annotation)) container.preHandle(annotation, annotationNode);
return;
}
try {
if (checkAndSetHandled(annotation)) container.handle(annotation, annotationNode);
} catch (AnnotationValueDecodeFail fail) {
fail.owner.setError(fail.getMessage(), fail.idx);
} catch (Throwable t) {
error(ast, String.format("Lombok annotation handler %s failed", container.handler.getClass()), t);
}
}
@@ -144,7 +144,7 @@ public class TransformEclipseAST {
try {
String message = "Lombok can't parse this source: " + t.toString();
EclipseAST.addProblemToCompilationResult(ast, false, message, 0, 0);
EclipseAST.addProblemToCompilationResult(ast.getFileName(), ast.compilationResult, false, message, 0, 0);
t.printStackTrace();
} catch (Throwable t2) {
try {
@@ -22,6 +22,7 @@
package lombok.eclipse.handlers;
import static lombok.eclipse.Eclipse.*;
import static lombok.core.TransformationsUtil.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
@@ -61,6 +62,7 @@ import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.eclipse.jdt.internal.compiler.ast.ArrayQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference;
import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.CastExpression;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
@@ -142,7 +144,7 @@ public class EclipseHandlerUtil {
} catch (NoClassDefFoundError e) { //standalone ecj does not jave Platform, ILog, IStatus, and friends.
new TerminalLogger().error(message, bundleName, error);
}
if (cud != null) EclipseAST.addProblemToCompilationResult(cud, false, message + " - See error log.", 0, 0);
if (cud != null) EclipseAST.addProblemToCompilationResult(cud.getFileName(), cud.compilationResult, false, message + " - See error log.", 0, 0);
}
/**
@@ -290,11 +292,34 @@ public class EclipseHandlerUtil {
if (!lastPartA.equals(lastPartB)) return false;
String typeName = toQualifiedName(typeRef.getTypeName());
TypeResolver resolver = new TypeResolver(node.getPackageDeclaration(), node.getImportStatements());
TypeResolver resolver = new TypeResolver(node.getImportList());
return resolver.typeMatches(node, type.getName(), typeName);
}
public static void sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(EclipseNode typeNode, EclipseNode errorNode) {
List<String> disallowed = null;
for (EclipseNode child : typeNode.down()) {
for (Class<? extends java.lang.annotation.Annotation> annType : INVALID_ON_BUILDERS) {
if (annotationTypeMatches(annType, child)) {
if (disallowed == null) disallowed = new ArrayList<String>();
disallowed.add(annType.getSimpleName());
}
}
}
int size = disallowed == null ? 0 : disallowed.size();
if (size == 0) return;
if (size == 1) {
errorNode.addError("@" + disallowed.get(0) + " is not allowed on builder classes.");
return;
}
StringBuilder out = new StringBuilder();
for (String a : disallowed) out.append("@").append(a).append(", ");
out.setLength(out.length() - 2);
errorNode.addError(out.append(" are not allowed on builder classes.").toString());
}
public static Annotation copyAnnotation(Annotation annotation, ASTNode source) {
int pS = source.sourceStart, pE = source.sourceEnd;
@@ -358,6 +383,20 @@ public class EclipseHandlerUtil {
return out;
}
public static TypeReference namePlusTypeParamsToTypeReference(char[] typeName, TypeParameter[] params, long p) {
if (params != null && params.length > 0) {
TypeReference[] refs = new TypeReference[params.length];
int idx = 0;
for (TypeParameter param : params) {
TypeReference typeRef = new SingleTypeReference(param.name, p);
refs[idx++] = typeRef;
}
return new ParameterizedSingleTypeReference(typeName, refs, 0, p);
}
return new SingleTypeReference(typeName, p);
}
/**
* Convenience method that creates a new array and copies each TypeReference in the source array via
* {@link #copyType(TypeReference, ASTNode)}.
@@ -830,15 +869,20 @@ public class EclipseHandlerUtil {
private static final Object MARKER = new Object();
static void registerCreatedLazyGetter(FieldDeclaration field, char[] methodName, TypeReference returnType) {
if (!nameEquals(returnType.getTypeName(), "boolean") || returnType.dimensions() > 0) return;
generatedLazyGettersWithPrimitiveBoolean.put(field, MARKER);
if (isBoolean(returnType)) {
generatedLazyGettersWithPrimitiveBoolean.put(field, MARKER);
}
}
public static boolean isBoolean(TypeReference typeReference) {
return nameEquals(typeReference.getTypeName(), "boolean") && typeReference.dimensions() == 0;
}
private static GetterMethod findGetter(EclipseNode field) {
FieldDeclaration fieldDeclaration = (FieldDeclaration) field.get();
boolean forceBool = generatedLazyGettersWithPrimitiveBoolean.containsKey(fieldDeclaration);
TypeReference fieldType = fieldDeclaration.type;
boolean isBoolean = forceBool || (nameEquals(fieldType.getTypeName(), "boolean") && fieldType.dimensions() == 0);
boolean isBoolean = forceBool || isBoolean(fieldType);
EclipseNode typeNode = field.up();
for (String potentialGetterName : toAllGetterNames(field, isBoolean)) {
@@ -1207,15 +1251,15 @@ public class EclipseHandlerUtil {
* Inserts a field into an existing type. The type must represent a {@code TypeDeclaration}.
* The field carries the &#64;{@link SuppressWarnings}("all") annotation.
*/
public static void injectFieldSuppressWarnings(EclipseNode type, FieldDeclaration field) {
public static EclipseNode injectFieldSuppressWarnings(EclipseNode type, FieldDeclaration field) {
field.annotations = createSuppressWarningsAll(field, field.annotations);
injectField(type, field);
return injectField(type, field);
}
/**
* Inserts a field into an existing type. The type must represent a {@code TypeDeclaration}.
*/
public static void injectField(EclipseNode type, FieldDeclaration field) {
public static EclipseNode injectField(EclipseNode type, FieldDeclaration field) {
TypeDeclaration parent = (TypeDeclaration) type.get();
if (parent.fields == null) {
@@ -1242,7 +1286,7 @@ public class EclipseHandlerUtil {
}
}
type.add(field, Kind.FIELD);
return type.add(field, Kind.FIELD);
}
private static boolean isEnumConstant(final FieldDeclaration field) {
@@ -1252,7 +1296,7 @@ public class EclipseHandlerUtil {
/**
* Inserts a method into an existing type. The type must represent a {@code TypeDeclaration}.
*/
public static void injectMethod(EclipseNode type, AbstractMethodDeclaration method) {
public static EclipseNode injectMethod(EclipseNode type, AbstractMethodDeclaration method) {
method.annotations = createSuppressWarningsAll(method, method.annotations);
TypeDeclaration parent = (TypeDeclaration) type.get();
@@ -1285,7 +1329,29 @@ public class EclipseHandlerUtil {
parent.methods = newArray;
}
type.add(method, Kind.METHOD);
return type.add(method, Kind.METHOD);
}
/**
* Adds an inner type (class, interface, enum) to the given type. Cannot inject top-level types.
*
* @param typeNode parent type to inject new type into
* @param type New type (class, interface, etc) to inject.
*/
public static EclipseNode injectType(final EclipseNode typeNode, final TypeDeclaration type) {
type.annotations = createSuppressWarningsAll(type, type.annotations);
TypeDeclaration parent = (TypeDeclaration) typeNode.get();
if (parent.memberTypes == null) {
parent.memberTypes = new TypeDeclaration[] { type };
} else {
TypeDeclaration[] newArray = new TypeDeclaration[parent.memberTypes.length + 1];
System.arraycopy(parent.memberTypes, 0, newArray, 0, parent.memberTypes.length);
newArray[parent.memberTypes.length] = type;
parent.memberTypes = newArray;
}
return typeNode.add(type, Kind.TYPE);
}
private static final char[] ALL = "all".toCharArray();
@@ -1334,7 +1400,11 @@ public class EclipseHandlerUtil {
EqualExpression equalExpression = new EqualExpression(varName, nullLiteral, OperatorIds.EQUAL_EQUAL);
equalExpression.sourceStart = pS; equalExpression.statementEnd = equalExpression.sourceEnd = pE;
setGeneratedBy(equalExpression, source);
IfStatement ifStatement = new IfStatement(equalExpression, throwStatement, 0, 0);
Block throwBlock = new Block(0);
throwBlock.statements = new Statement[] {throwStatement};
throwBlock.sourceStart = pS; throwBlock.sourceEnd = pE;
setGeneratedBy(throwBlock, source);
IfStatement ifStatement = new IfStatement(equalExpression, throwBlock, 0, 0);
setGeneratedBy(ifStatement, source);
return ifStatement;
}
@@ -0,0 +1,376 @@
/*
* Copyright (C) 2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.eclipse.handlers;
import static lombok.eclipse.Eclipse.*;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.mangosdk.spi.ProviderFor;
import lombok.AccessLevel;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.core.TransformationsUtil;
import lombok.eclipse.Eclipse;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
import lombok.eclipse.handlers.HandleConstructor.SkipIfConstructorExists;
import lombok.experimental.Builder;
import lombok.experimental.NonFinal;
@ProviderFor(EclipseAnnotationHandler.class)
@HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes.
public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
@Override public void handle(AnnotationValues<Builder> annotation, Annotation ast, EclipseNode annotationNode) {
long p = (long) ast.sourceStart << 32 | ast.sourceEnd;
Builder builderInstance = annotation.getInstance();
String builderMethodName = builderInstance.builderMethodName();
String buildMethodName = builderInstance.buildMethodName();
String builderClassName = builderInstance.builderClassName();
if (builderMethodName == null) builderMethodName = "builder";
if (buildMethodName == null) builderMethodName = "build";
if (builderClassName == null) builderClassName = "";
if (!checkName("builderMethodName", builderMethodName, annotationNode)) return;
if (!checkName("buildMethodName", buildMethodName, annotationNode)) return;
if (!builderClassName.isEmpty()) {
if (!checkName("builderClassName", builderClassName, annotationNode)) return;
}
EclipseNode parent = annotationNode.up();
List<TypeReference> typesOfParameters = new ArrayList<TypeReference>();
List<char[]> namesOfParameters = new ArrayList<char[]>();
TypeReference returnType;
TypeParameter[] typeParams;
TypeReference[] thrownExceptions;
char[] nameOfStaticBuilderMethod;
EclipseNode tdParent;
AbstractMethodDeclaration fillParametersFrom = null;
if (parent.get() instanceof TypeDeclaration) {
tdParent = parent;
TypeDeclaration td = (TypeDeclaration) tdParent.get();
List<EclipseNode> fields = new ArrayList<EclipseNode>();
@SuppressWarnings("deprecation")
boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation(lombok.experimental.Value.class, parent));
for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent)) {
FieldDeclaration fd = (FieldDeclaration) fieldNode.get();
// final fields with an initializer cannot be written to, so they can't be 'builderized'. Unfortunately presence of @Value makes
// non-final fields final, but @Value's handler hasn't done this yet, so we have to do this math ourselves.
// Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that.
if (fd.initialization != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue;
namesOfParameters.add(fd.name);
typesOfParameters.add(fd.type);
fields.add(fieldNode);
}
new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, fields, null, SkipIfConstructorExists.I_AM_BUILDER, true, Collections.<Annotation>emptyList(), ast);
returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p);
typeParams = td.typeParameters;
thrownExceptions = null;
nameOfStaticBuilderMethod = null;
if (builderClassName.isEmpty()) builderClassName = new String(td.name) + "Builder";
} else if (parent.get() instanceof ConstructorDeclaration) {
ConstructorDeclaration cd = (ConstructorDeclaration) parent.get();
if (cd.typeParameters != null && cd.typeParameters.length > 0) {
annotationNode.addError("@Builder is not supported on constructors with constructor type parameters.");
return;
}
tdParent = parent.up();
TypeDeclaration td = (TypeDeclaration) tdParent.get();
fillParametersFrom = cd;
returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p);
typeParams = td.typeParameters;
thrownExceptions = cd.thrownExceptions;
nameOfStaticBuilderMethod = null;
if (builderClassName.isEmpty()) builderClassName = new String(cd.selector) + "Builder";
} else if (parent.get() instanceof MethodDeclaration) {
MethodDeclaration md = (MethodDeclaration) parent.get();
tdParent = parent.up();
if (!md.isStatic()) {
annotationNode.addError("@Builder is only supported on types, constructors, and static methods.");
return;
}
fillParametersFrom = md;
returnType = copyType(md.returnType, ast);
typeParams = md.typeParameters;
thrownExceptions = md.thrownExceptions;
nameOfStaticBuilderMethod = md.selector;
if (builderClassName.isEmpty()) {
char[] token;
if (md.returnType instanceof QualifiedTypeReference) {
char[][] tokens = ((QualifiedTypeReference) md.returnType).tokens;
token = tokens[tokens.length - 1];
} else if (md.returnType instanceof SingleTypeReference) {
token = ((SingleTypeReference) md.returnType).token;
if (!(md.returnType instanceof ParameterizedSingleTypeReference) && typeParams != null) {
for (TypeParameter tp : typeParams) {
if (Arrays.equals(tp.name, token)) {
annotationNode.addError("@Builder requires specifying 'builderClassName' if used on methods with a type parameter as return type.");
return;
}
}
}
} else {
annotationNode.addError("Unexpected kind of return type on annotated method. Specify 'builderClassName' to solve this problem.");
return;
}
if (Character.isLowerCase(token[0])) {
char[] newToken = new char[token.length];
System.arraycopy(token, 1, newToken, 1, token.length - 1);
newToken[0] = Character.toTitleCase(token[0]);
token = newToken;
}
builderClassName = new String(token) + "Builder";
}
} else {
annotationNode.addError("@Builder is only supported on types, constructors, and static methods.");
return;
}
if (fillParametersFrom != null) {
if (fillParametersFrom.arguments != null) for (Argument a : fillParametersFrom.arguments) {
namesOfParameters.add(a.name);
typesOfParameters.add(a.type);
}
}
EclipseNode builderType = findInnerClass(tdParent, builderClassName);
if (builderType == null) {
builderType = makeBuilderClass(tdParent, builderClassName, typeParams, ast);
} else {
sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode);
}
List<EclipseNode> fieldNodes = addFieldsToBuilder(builderType, namesOfParameters, typesOfParameters, ast);
List<AbstractMethodDeclaration> newMethods = new ArrayList<AbstractMethodDeclaration>();
for (EclipseNode fieldNode : fieldNodes) {
MethodDeclaration newMethod = makeSetterMethodForBuilder(builderType, fieldNode, ast, builderInstance.fluent(), builderInstance.chain());
if (newMethod != null) newMethods.add(newMethod);
}
if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) {
ConstructorDeclaration cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, builderType, Collections.<EclipseNode>emptyList(), true, ast, Collections.<Annotation>emptyList());
if (cd != null) injectMethod(builderType, cd);
}
for (AbstractMethodDeclaration newMethod : newMethods) injectMethod(builderType, newMethod);
if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) {
MethodDeclaration md = generateBuildMethod(buildMethodName, nameOfStaticBuilderMethod, returnType, namesOfParameters, builderType, ast, thrownExceptions);
if (md != null) injectMethod(builderType, md);
}
if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
MethodDeclaration md = HandleToString.createToString(builderType, fieldNodes, true, false, ast, FieldAccess.ALWAYS_FIELD);
if (md != null) injectMethod(builderType, md);
}
if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
MethodDeclaration md = generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams, ast);
if (md != null) injectMethod(tdParent, md);
}
}
private MethodDeclaration generateBuilderMethod(String builderMethodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long) pS << 32 | pE;
MethodDeclaration out = new MethodDeclaration(
((CompilationUnitDeclaration) type.top().get()).compilationResult);
out.selector = builderMethodName.toCharArray();
out.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccStatic;
out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
out.returnType = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p);
out.typeParameters = copyTypeParams(typeParams, source);
AllocationExpression invoke = new AllocationExpression();
invoke.type = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p);
out.statements = new Statement[] {new ReturnStatement(invoke, pS, pE)};
out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope);
return out;
}
private MethodDeclaration generateBuildMethod(String name, char[] staticName, TypeReference returnType, List<char[]> fieldNames, EclipseNode type, ASTNode source, TypeReference[] thrownExceptions) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long) pS << 32 | pE;
MethodDeclaration out = new MethodDeclaration(
((CompilationUnitDeclaration) type.top().get()).compilationResult);
out.modifiers = ClassFileConstants.AccPublic;
TypeDeclaration typeDecl = (TypeDeclaration) type.get();
out.selector = name.toCharArray();
out.thrownExceptions = copyTypes(thrownExceptions, source);
out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
out.returnType = returnType;
List<Expression> assigns = new ArrayList<Expression>();
for (char[] fieldName : fieldNames) {
SingleNameReference nameRef = new SingleNameReference(fieldName, p);
assigns.add(nameRef);
}
Statement statement;
if (staticName == null) {
AllocationExpression allocationStatement = new AllocationExpression();
allocationStatement.type = copyType(out.returnType, source);
allocationStatement.arguments = assigns.isEmpty() ? null : assigns.toArray(new Expression[assigns.size()]);
statement = new ReturnStatement(allocationStatement, (int)(p >> 32), (int)p);
} else {
MessageSend invoke = new MessageSend();
invoke.selector = staticName;
invoke.receiver = new SingleNameReference(type.up().getName().toCharArray(), p);
TypeParameter[] tps = ((TypeDeclaration) type.get()).typeParameters;
if (tps != null) {
TypeReference[] trs = new TypeReference[tps.length];
for (int i = 0; i < trs.length; i++) {
trs[i] = new SingleTypeReference(tps[i].name, p);
}
invoke.typeArguments = trs;
}
invoke.arguments = assigns.isEmpty() ? null : assigns.toArray(new Expression[assigns.size()]);
if (returnType instanceof SingleTypeReference && Arrays.equals(TypeBinding.VOID.simpleName, ((SingleTypeReference) returnType).token)) {
statement = invoke;
} else {
statement = new ReturnStatement(invoke, (int)(p >> 32), (int)p);
}
}
out.statements = new Statement[] { statement };
out.traverse(new SetGeneratedByVisitor(source), typeDecl.scope);
return out;
}
private List<EclipseNode> addFieldsToBuilder(EclipseNode builderType, List<char[]> namesOfParameters, List<TypeReference> typesOfParameters, ASTNode source) {
int len = namesOfParameters.size();
TypeDeclaration td = (TypeDeclaration) builderType.get();
FieldDeclaration[] existing = td.fields;
if (existing == null) existing = new FieldDeclaration[0];
List<EclipseNode> out = new ArrayList<EclipseNode>();
top:
for (int i = len - 1; i >= 0; i--) {
char[] name = namesOfParameters.get(i);
for (FieldDeclaration exists : existing) {
if (Arrays.equals(exists.name, name)) {
out.add(builderType.getNodeFor(exists));
continue top;
}
}
TypeReference fieldReference = copyType(typesOfParameters.get(i), source);
FieldDeclaration newField = new FieldDeclaration(name, 0, 0);
newField.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
newField.modifiers = ClassFileConstants.AccPrivate;
newField.type = fieldReference;
out.add(injectField(builderType, newField));
}
Collections.reverse(out);
return out;
}
private static final AbstractMethodDeclaration[] EMPTY = {};
private MethodDeclaration makeSetterMethodForBuilder(EclipseNode builderType, EclipseNode fieldNode, ASTNode source, boolean fluent, boolean chain) {
TypeDeclaration td = (TypeDeclaration) builderType.get();
AbstractMethodDeclaration[] existing = td.methods;
if (existing == null) existing = EMPTY;
int len = existing.length;
FieldDeclaration fd = (FieldDeclaration) fieldNode.get();
char[] name = fd.name;
for (int i = 0; i < len; i++) {
if (!(existing[i] instanceof MethodDeclaration)) continue;
char[] existingName = existing[i].selector;
if (Arrays.equals(name, existingName)) return null;
}
boolean isBoolean = isBoolean(fd.type);
String setterName = fluent ? fieldNode.getName() : TransformationsUtil.toSetterName(null, fieldNode.getName(), isBoolean);
return HandleSetter.createSetter(td, fieldNode, setterName, chain, ClassFileConstants.AccPublic,
source, Collections.<Annotation>emptyList(), Collections.<Annotation>emptyList());
}
private EclipseNode findInnerClass(EclipseNode parent, String name) {
char[] c = name.toCharArray();
for (EclipseNode child : parent.down()) {
if (child.getKind() != Kind.TYPE) continue;
TypeDeclaration td = (TypeDeclaration) child.get();
if (Arrays.equals(td.name, c)) return child;
}
return null;
}
private EclipseNode makeBuilderClass(EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, ASTNode source) {
TypeDeclaration parent = (TypeDeclaration) tdParent.get();
TypeDeclaration builder = new TypeDeclaration(parent.compilationResult);
builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
builder.modifiers |= ClassFileConstants.AccPublic | ClassFileConstants.AccStatic;
builder.typeParameters = copyTypeParams(typeParams, source);
builder.name = builderClassName.toCharArray();
builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
return injectType(tdParent, builder);
}
}
@@ -39,6 +39,7 @@ import lombok.core.AnnotationValues;
import lombok.core.TransformationsUtil;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
import lombok.experimental.Builder;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
@@ -53,18 +54,14 @@ import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldReference;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.mangosdk.spi.ProviderFor;
@@ -82,7 +79,7 @@ public class HandleConstructor {
List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor=", annotationNode);
new HandleConstructor().generateConstructor(typeNode, level, fields, staticName, false, false, onConstructor, ast);
new HandleConstructor().generateConstructor(typeNode, level, fields, staticName, SkipIfConstructorExists.NO, false, onConstructor, ast);
}
}
@@ -100,7 +97,7 @@ public class HandleConstructor {
List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@RequiredArgsConstructor(onConstructor=", annotationNode);
new HandleConstructor().generateConstructor(typeNode, level, findRequiredFields(typeNode), staticName, false, suppressConstructorProperties, onConstructor, ast);
new HandleConstructor().generateConstructor(typeNode, level, findRequiredFields(typeNode), staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, onConstructor, ast);
}
}
@@ -117,7 +114,7 @@ public class HandleConstructor {
return fields;
}
private static List<EclipseNode> findAllFields(EclipseNode typeNode) {
static List<EclipseNode> findAllFields(EclipseNode typeNode) {
List<EclipseNode> fields = new ArrayList<EclipseNode>();
for (EclipseNode child : typeNode.down()) {
if (child.getKind() != Kind.FIELD) continue;
@@ -146,7 +143,7 @@ public class HandleConstructor {
List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@AllArgsConstructor(onConstructor=", annotationNode);
new HandleConstructor().generateConstructor(typeNode, level, findAllFields(typeNode), staticName, false, suppressConstructorProperties, onConstructor, ast);
new HandleConstructor().generateConstructor(typeNode, level, findAllFields(typeNode), staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, onConstructor, ast);
}
}
@@ -164,25 +161,34 @@ public class HandleConstructor {
return true;
}
public void generateRequiredArgsConstructor(EclipseNode typeNode, AccessLevel level, String staticName, boolean skipIfConstructorExists, List<Annotation> onConstructor, ASTNode source) {
public void generateRequiredArgsConstructor(EclipseNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, List<Annotation> onConstructor, ASTNode source) {
generateConstructor(typeNode, level, findRequiredFields(typeNode), staticName, skipIfConstructorExists, false, onConstructor, source);
}
public void generateAllArgsConstructor(EclipseNode typeNode, AccessLevel level, String staticName, boolean skipIfConstructorExists, List<Annotation> onConstructor, ASTNode source) {
public void generateAllArgsConstructor(EclipseNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, List<Annotation> onConstructor, ASTNode source) {
generateConstructor(typeNode, level, findAllFields(typeNode), staticName, skipIfConstructorExists, false, onConstructor, source);
}
public void generateConstructor(EclipseNode typeNode, AccessLevel level, List<EclipseNode> fields, String staticName, boolean skipIfConstructorExists, boolean suppressConstructorProperties, List<Annotation> onConstructor, ASTNode source) {
public enum SkipIfConstructorExists {
YES, NO, I_AM_BUILDER;
}
public void generateConstructor(EclipseNode typeNode, AccessLevel level, List<EclipseNode> fields, String staticName, SkipIfConstructorExists skipIfConstructorExists, boolean suppressConstructorProperties, List<Annotation> onConstructor, ASTNode source) {
boolean staticConstrRequired = staticName != null && !staticName.equals("");
if (skipIfConstructorExists && constructorExists(typeNode) != MemberExistsResult.NOT_EXISTS) return;
if (skipIfConstructorExists) {
if (skipIfConstructorExists != SkipIfConstructorExists.NO && constructorExists(typeNode) != MemberExistsResult.NOT_EXISTS) return;
if (skipIfConstructorExists != SkipIfConstructorExists.NO) {
for (EclipseNode child : typeNode.down()) {
if (child.getKind() == Kind.ANNOTATION) {
if (annotationTypeMatches(NoArgsConstructor.class, child) ||
boolean skipGeneration = (annotationTypeMatches(NoArgsConstructor.class, child) ||
annotationTypeMatches(AllArgsConstructor.class, child) ||
annotationTypeMatches(RequiredArgsConstructor.class, child)) {
annotationTypeMatches(RequiredArgsConstructor.class, child));
if (!skipGeneration && skipIfConstructorExists == SkipIfConstructorExists.YES) {
skipGeneration = annotationTypeMatches(Builder.class, child);
}
if (skipGeneration) {
if (staticConstrRequired) {
// @Data has asked us to generate a constructor, but we're going to skip this instruction, as an explicit 'make a constructor' annotation
// will take care of it. However, @Data also wants a specific static name; this will be ignored; the appropriate way to do this is to use
@@ -235,7 +241,7 @@ public class HandleConstructor {
return new Annotation[] { ann };
}
private ConstructorDeclaration createConstructor(
static ConstructorDeclaration createConstructor(
AccessLevel level, EclipseNode type, Collection<EclipseNode> fields,
boolean suppressConstructorProperties, ASTNode source, List<Annotation> onConstructor) {
@@ -307,7 +313,7 @@ public class HandleConstructor {
return constructor;
}
private boolean isLocalType(EclipseNode type) {
private static boolean isLocalType(EclipseNode type) {
Kind kind = type.up().getKind();
if (kind == Kind.COMPILATION_UNIT) return false;
if (kind == Kind.TYPE) return isLocalType(type.up());
@@ -321,18 +327,9 @@ public class HandleConstructor {
MethodDeclaration constructor = new MethodDeclaration(
((CompilationUnitDeclaration) type.top().get()).compilationResult);
constructor.modifiers = toEclipseModifier(level) | Modifier.STATIC;
constructor.modifiers = toEclipseModifier(level) | ClassFileConstants.AccStatic;
TypeDeclaration typeDecl = (TypeDeclaration) type.get();
if (typeDecl.typeParameters != null && typeDecl.typeParameters.length > 0) {
TypeReference[] refs = new TypeReference[typeDecl.typeParameters.length];
int idx = 0;
for (TypeParameter param : typeDecl.typeParameters) {
TypeReference typeRef = new SingleTypeReference(param.name, (long)param.sourceStart << 32 | param.sourceEnd);
setGeneratedBy(typeRef, source);
refs[idx++] = typeRef;
}
constructor.returnType = new ParameterizedSingleTypeReference(typeDecl.name, refs, 0, p);
} else constructor.returnType = new SingleTypeReference(((TypeDeclaration)type.get()).name, p);
constructor.returnType = EclipseHandlerUtil.namePlusTypeParamsToTypeReference(typeDecl.name, typeDecl.typeParameters, p);
constructor.annotations = null;
constructor.selector = name.toCharArray();
constructor.thrownExceptions = null;
@@ -28,6 +28,7 @@ import lombok.Data;
import lombok.core.AnnotationValues;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
import lombok.eclipse.handlers.HandleConstructor.SkipIfConstructorExists;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
@@ -64,6 +65,6 @@ public class HandleData extends EclipseAnnotationHandler<Data> {
new HandleSetter().generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode);
new HandleToString().generateToStringForType(typeNode, annotationNode);
new HandleConstructor().generateRequiredArgsConstructor(typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), true, Collections.<Annotation>emptyList(), ast);
new HandleConstructor().generateRequiredArgsConstructor(typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), SkipIfConstructorExists.YES, Collections.<Annotation>emptyList(), ast);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2012 The Project Lombok Authors.
* Copyright (C) 2009-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -204,18 +204,27 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
}
boolean isFinal = (typeDecl.modifiers & ClassFileConstants.AccFinal) != 0;
boolean needsCanEqual = !isDirectDescendantOfObject || !isFinal;
java.util.List<MemberExistsResult> existsResults = new ArrayList<MemberExistsResult>();
existsResults.add(methodExists("equals", typeNode, 1));
existsResults.add(methodExists("hashCode", typeNode, 0));
existsResults.add(methodExists("canEqual", typeNode, 1));
switch (Collections.max(existsResults)) {
boolean needsCanEqual = !isFinal || !isDirectDescendantOfObject;
MemberExistsResult equalsExists = methodExists("equals", typeNode, 1);
MemberExistsResult hashCodeExists = methodExists("hashCode", typeNode, 0);
MemberExistsResult canEqualExists = methodExists("canEqual", typeNode, 1);
switch (Collections.max(Arrays.asList(equalsExists, hashCodeExists, canEqualExists))) {
case EXISTS_BY_LOMBOK:
return;
case EXISTS_BY_USER:
if (whineIfExists) {
String msg = String.format("Not generating equals%s: A method with one of those names already exists. (Either all or none of these methods will be generated).", needsCanEqual ? ", hashCode and canEquals" : " and hashCode");
errorNode.addWarning(msg);
} else if (equalsExists == MemberExistsResult.NOT_EXISTS || hashCodeExists == MemberExistsResult.NOT_EXISTS) {
// This means equals OR hashCode exists and not both (or neither, but canEqual is there).
// Even though we should suppress the message about not generating these, this is such a weird and surprising situation we should ALWAYS generate a warning.
// The user code couldn't possibly (barring really weird subclassing shenanigans) be in a shippable state anyway; the implementations of these 3 methods are
// all inter-related and should be written by the same entity.
String msg = String.format("Not generating %s: One of equals, hashCode, and canEqual exists. " +
"You should either write all of these are none of these (in the latter case, lombok generates them).",
equalsExists == MemberExistsResult.NOT_EXISTS && hashCodeExists == MemberExistsResult.NOT_EXISTS ? "equals and hashCode" :
equalsExists == MemberExistsResult.NOT_EXISTS ? "equals" : "hashCode");
errorNode.addWarning(msg);
}
return;
case NOT_EXISTS:
@@ -43,7 +43,7 @@ import org.mangosdk.spi.ProviderFor;
* Handles the {@code lombok.FieldDefaults} annotation for eclipse.
*/
@ProviderFor(EclipseAnnotationHandler.class)
@HandlerPriority(-512) //-2^9; to ensure @Setter and such pick up on messing with the fields' 'final' state, run earlier.
@HandlerPriority(-2048) //-2^11; to ensure @Value picks up on messing with the fields' 'final' state, run earlier.
public class HandleFieldDefaults extends EclipseAnnotationHandler<FieldDefaults> {
public boolean generateFieldDefaultsForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean makeFinal, boolean checkForTypeLevelFieldDefaults) {
if (checkForTypeLevelFieldDefaults) {
@@ -112,6 +112,14 @@ public class HandleFieldDefaults extends EclipseAnnotationHandler<FieldDefaults>
return;
}
if (level == AccessLevel.PACKAGE) {
annotationNode.addError("Setting 'level' to PACKAGE does nothing. To force fields as package private, use the @PackagePrivate annotation on the field.");
}
if (!makeFinal && annotation.isExplicit("makeFinal")) {
annotationNode.addError("Setting 'makeFinal' to false does nothing. To force fields to be non-final, use the @NonFinal annotation on the field.");
}
if (node == null) return;
generateFieldDefaultsForType(node, annotationNode, level, makeFinal, false);
@@ -49,6 +49,8 @@ import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference;
import org.eclipse.jdt.internal.compiler.ast.Assignment;
import org.eclipse.jdt.internal.compiler.ast.BinaryExpression;
import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.CastExpression;
import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
@@ -67,6 +69,7 @@ import org.eclipse.jdt.internal.compiler.ast.SynchronizedStatement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.mangosdk.spi.ProviderFor;
/**
@@ -184,7 +187,7 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
}
TypeReference fieldType = copyType(field.type, source);
boolean isBoolean = nameEquals(fieldType.getTypeName(), "boolean") && fieldType.dimensions() == 0;
boolean isBoolean = isBoolean(fieldType);
String getterName = toGetterName(fieldNode, isBoolean);
if (getterName == null) {
@@ -286,7 +289,6 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
}
private static final char[][] AR = fromQualifiedName("java.util.concurrent.atomic.AtomicReference");
private static final TypeReference[][] AR_PARAMS = new TypeReference[5][];
private static final java.util.Map<String, char[][]> TYPE_MAP;
static {
@@ -305,41 +307,54 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
private static char[] valueName = "value".toCharArray();
private static char[] actualValueName = "actualValue".toCharArray();
private static final int PARENTHESIZED = (1 << ASTNode.ParenthesizedSHIFT) & ASTNode.ParenthesizedMASK;
private Statement[] createLazyGetterBody(ASTNode source, EclipseNode fieldNode) {
/*
java.util.concurrent.atomic.AtomicReference<ValueType> value = this.fieldName.get();
java.lang.Object value = this.fieldName.get();
if (value == null) {
synchronized (this.fieldName) {
value = this.fieldName.get();
if (value == null) {
final ValueType actualValue = new ValueType();
value = new java.util.concurrent.atomic.AtomicReference<ValueType>(actualValue);
final RawValueType actualValue = INITIALIZER_EXPRESSION;
[IF PRIMITIVE]
value = actualValue;
[ELSE]
value = actualValue == null ? this.fieldName : actualValue;
[END IF]
this.fieldName.set(value);
}
}
}
return value.get();
[IF PRIMITIVE]
return (BoxedValueType) value;
[ELSE]
return (BoxedValueType) (value == this.fieldName ? null : value);
[END IF]
*/
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
TypeReference componentType = copyType(field.type, source);
TypeReference rawComponentType = copyType(field.type, source);
TypeReference boxedComponentType = null;
boolean isPrimitive = false;
if (field.type instanceof SingleTypeReference && !(field.type instanceof ArrayTypeReference)) {
char[][] newType = TYPE_MAP.get(new String(((SingleTypeReference)field.type).token));
if (newType != null) {
componentType = new QualifiedTypeReference(newType, poss(source, 3));
boxedComponentType = new QualifiedTypeReference(newType, poss(source, 3));
isPrimitive = true;
}
}
if (boxedComponentType == null) boxedComponentType = copyType(field.type, source);
boxedComponentType.sourceStart = pS; boxedComponentType.sourceEnd = boxedComponentType.statementEnd = pE;
Statement[] statements = new Statement[3];
/* java.util.concurrent.atomic.AtomicReference<ValueType> value = this.fieldName.get(); */ {
/* java.lang.Object value = this.fieldName.get(); */ {
LocalDeclaration valueDecl = new LocalDeclaration(valueName, pS, pE);
TypeReference[][] typeParams = AR_PARAMS.clone();
typeParams[4] = new TypeReference[] {copyType(componentType, source)};
valueDecl.type = new ParameterizedQualifiedTypeReference(AR, typeParams, 0, poss(source, 5));
valueDecl.type = new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(source, 3));
valueDecl.type.sourceStart = pS; valueDecl.type.sourceEnd = valueDecl.type.statementEnd = pE;
MessageSend getter = new MessageSend();
@@ -356,8 +371,12 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
synchronized (this.fieldName) {
value = this.fieldName.get();
if (value == null) {
final ValueType actualValue = new ValueType();
value = new java.util.concurrent.atomic.AtomicReference<ValueType>(actualValue);
final ValueType actualValue = INITIALIZER_EXPRESSION;
[IF PRIMITIVE]
value = actualValue;
[ELSE]
value = actualValue == null ? this.fieldName : actualValue;
[END IF]
this.fieldName.set(value);
}
}
@@ -383,28 +402,37 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
EqualExpression innerCond = new EqualExpression(
new SingleNameReference(valueName, p), new NullLiteral(pS, pE),
BinaryExpression.EQUAL_EQUAL);
innerCond.sourceStart = pS; innerCond.sourceEnd = innerCond.statementEnd = pE;
Block innerThen = new Block(0);
innerThen.statements = new Statement[3];
/* final ValueType actualValue = new ValueType(); */ {
/* final ValueType actualValue = INITIALIZER_EXPRESSION */ {
LocalDeclaration actualValueDecl = new LocalDeclaration(actualValueName, pS, pE);
actualValueDecl.type = copyType(field.type, source);
actualValueDecl.type = rawComponentType;
actualValueDecl.type.sourceStart = pS; actualValueDecl.type.sourceEnd = actualValueDecl.type.statementEnd = pE;
actualValueDecl.initialization = field.initialization;
actualValueDecl.modifiers = ClassFileConstants.AccFinal;
innerThen.statements[0] = actualValueDecl;
}
/* value = new java.util.concurrent.atomic.AtomicReference<ValueType>(actualValue); */ {
AllocationExpression create = new AllocationExpression();
create.sourceStart = pS; create.sourceEnd = create.statementEnd = pE;
TypeReference[][] typeParams = AR_PARAMS.clone();
typeParams[4] = new TypeReference[] {copyType(componentType, source)};
create.type = new ParameterizedQualifiedTypeReference(AR, typeParams, 0, poss(source, 5));
create.type.sourceStart = pS; create.type.sourceEnd = create.type.statementEnd = pE;
create.arguments = new Expression[] {new SingleNameReference(actualValueName, p)};
Assignment innerAssign = new Assignment(new SingleNameReference(valueName, p), create, pE);
innerAssign.sourceStart = pS; innerAssign.statementEnd = innerAssign.sourceEnd = pE;
innerThen.statements[1] = innerAssign;
/* [IF PRIMITIVE] value = actualValue; */ {
if (isPrimitive) {
Assignment innerAssign = new Assignment(new SingleNameReference(valueName, p), new SingleNameReference(actualValueName, p), pE);
innerAssign.sourceStart = pS; innerAssign.statementEnd = innerAssign.sourceEnd = pE;
innerThen.statements[1] = innerAssign;
}
}
/* [ELSE] value = actualValue == null ? this.fieldName : actualValue; */ {
if (!isPrimitive) {
EqualExpression avIsNull = new EqualExpression(
new SingleNameReference(actualValueName, p), new NullLiteral(pS, pE),
BinaryExpression.EQUAL_EQUAL);
avIsNull.sourceStart = pS; avIsNull.sourceEnd = avIsNull.statementEnd = pE;
Expression fieldRef = createFieldAccessor(fieldNode, FieldAccess.ALWAYS_FIELD, source);
ConditionalExpression ternary = new ConditionalExpression(avIsNull, fieldRef, new SingleNameReference(actualValueName, p));
ternary.sourceStart = pS; ternary.sourceEnd = ternary.statementEnd = pE;
Assignment innerAssign = new Assignment(new SingleNameReference(valueName, p), ternary, pE);
innerAssign.sourceStart = pS; innerAssign.statementEnd = innerAssign.sourceEnd = pE;
innerThen.statements[1] = innerAssign;
}
}
/* this.fieldName.set(value); */ {
@@ -428,26 +456,34 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
statements[1] = ifStatement;
}
/* return value.get(); */ {
MessageSend getter = new MessageSend();
getter.sourceStart = pS; getter.sourceEnd = getter.statementEnd = pE;
getter.selector = new char[] {'g', 'e', 't'};
getter.receiver = new SingleNameReference(valueName, p);
statements[2] = new ReturnStatement(getter, pS, pE);
/* [IF PRIMITIVE] return (BoxedValueType)value; */ {
if (isPrimitive) {
CastExpression cast = makeCastExpression(new SingleNameReference(valueName, p), boxedComponentType, source);
statements[2] = new ReturnStatement(cast, pS, pE);
}
}
/* [ELSE] return (BoxedValueType)(value == this.fieldName ? null : value); */ {
if (!isPrimitive) {
EqualExpression vIsThisFieldName = new EqualExpression(
new SingleNameReference(valueName, p), createFieldAccessor(fieldNode, FieldAccess.ALWAYS_FIELD, source),
BinaryExpression.EQUAL_EQUAL);
vIsThisFieldName.sourceStart = pS; vIsThisFieldName.sourceEnd = vIsThisFieldName.statementEnd = pE;
ConditionalExpression ternary = new ConditionalExpression(vIsThisFieldName, new NullLiteral(pS, pE), new SingleNameReference(valueName, p));
ternary.sourceStart = pS; ternary.sourceEnd = ternary.statementEnd = pE;
ternary.bits |= PARENTHESIZED;
CastExpression cast = makeCastExpression(ternary, boxedComponentType, source);
statements[2] = new ReturnStatement(cast, pS, pE);
}
}
// update the field type and init last
/* private final java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<ValueType> fieldName = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<ValueType>>(); */ {
LocalDeclaration first = (LocalDeclaration) statements[0];
TypeReference innerType = copyType(first.type, source);
TypeReference[][] typeParams = AR_PARAMS.clone();
typeParams[4] = new TypeReference[] {copyType(innerType, source)};
/* private final java.util.concurrent.atomic.AtomicReference<java.lang.Object> fieldName = new java.util.concurrent.atomic.AtomicReference<java.lang.Object>(); */ {
TypeReference innerType = new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(source, 3));
TypeReference[][] typeParams = new TypeReference[5][];
typeParams[4] = new TypeReference[] {innerType};
TypeReference type = new ParameterizedQualifiedTypeReference(AR, typeParams, 0, poss(source, 5));
// Some magic here
type.sourceStart = -1; type.sourceEnd = -2;
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2012 The Project Lombok Authors.
* Copyright (C) 2010-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -177,6 +177,16 @@ public class HandleLog {
}
}
/**
* Handles the {@link lombok.extern.log4j.Log4j2} annotation for Eclipse.
*/
@ProviderFor(EclipseAnnotationHandler.class)
public static class HandleLog4j2Log extends EclipseAnnotationHandler<lombok.extern.log4j.Log4j2> {
@Override public void handle(AnnotationValues<lombok.extern.log4j.Log4j2> annotation, Annotation source, EclipseNode annotationNode) {
processAnnotation(LoggingFramework.LOG4J2, annotation, source, annotationNode);
}
}
/**
* Handles the {@link lombok.extern.slf4j.Slf4j} annotation for Eclipse.
*/
@@ -224,6 +234,9 @@ public class HandleLog {
// private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(TargetType.class);
LOG4J("org.apache.log4j.Logger", "org.apache.log4j.Logger", "getLogger", "@Log4j"),
// private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(TargetType.class);
LOG4J2("org.apache.logging.log4j.Logger", "org.apache.logging.log4j.LogManager", "getLogger", "@Log4j2"),
// private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TargetType.class);
SLF4J("org.slf4j.Logger", "org.slf4j.LoggerFactory", "getLogger", "@Slf4j"),
@@ -159,7 +159,7 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> {
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
TypeReference fieldType = copyType(field.type, source);
boolean isBoolean = nameEquals(fieldType.getTypeName(), "boolean") && fieldType.dimensions() == 0;
boolean isBoolean = isBoolean(fieldType);
String setterName = toSetterName(fieldNode, isBoolean);
boolean shouldReturnThis = shouldReturnThis(fieldNode);
@@ -192,7 +192,7 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> {
injectMethod(fieldNode.up(), method);
}
private MethodDeclaration createSetter(TypeDeclaration parent, EclipseNode fieldNode, String name, boolean shouldReturnThis, int modifier, ASTNode source, List<Annotation> onMethod, List<Annotation> onParam) {
static MethodDeclaration createSetter(TypeDeclaration parent, EclipseNode fieldNode, String name, boolean shouldReturnThis, int modifier, ASTNode source, List<Annotation> onMethod, List<Annotation> onParam) {
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
@@ -40,6 +40,8 @@ import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
@@ -147,7 +149,21 @@ public class HandleSneakyThrows extends EclipseAnnotationHandler<SneakyThrows> {
return;
}
if (method.statements == null) return;
if (method.statements == null || method.statements.length == 0) {
boolean hasConstructorCall = false;
if (method instanceof ConstructorDeclaration) {
ExplicitConstructorCall constructorCall = ((ConstructorDeclaration) method).constructorCall;
hasConstructorCall = constructorCall != null && !constructorCall.isImplicitSuper() && !constructorCall.isImplicitThis();
}
if (hasConstructorCall) {
annotation.addWarning("Calls to sibling / super constructors are always excluded from @SneakyThrows; @SneakyThrows has been ignored because there is no other code in this constructor.");
} else {
annotation.addWarning("This method or constructor is empty; @SneakyThrows has been ignored.");
}
return;
}
Statement[] contents = method.statements;
@@ -160,9 +176,9 @@ public class HandleSneakyThrows extends EclipseAnnotationHandler<SneakyThrows> {
}
private Statement buildTryCatchBlock(Statement[] contents, DeclaredException exception, ASTNode source, AbstractMethodDeclaration method) {
int methodStart = method.bodyStart;
int methodEnd = method.bodyEnd;
long methodPosEnd = methodEnd << 32 | (methodEnd & 0xFFFFFFFFL);
int methodStart = method.bodyStart;
int methodEnd = method.bodyEnd;
long methodPosEnd = ((long) methodEnd) << 32 | (methodEnd & 0xFFFFFFFFL);
TryStatement tryStatement = new TryStatement();
setGeneratedBy(tryStatement, source);
@@ -170,7 +170,7 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
}
}
private MethodDeclaration createToString(EclipseNode type, Collection<EclipseNode> fields,
static MethodDeclaration createToString(EclipseNode type, Collection<EclipseNode> fields,
boolean includeFieldNames, boolean callSuper, ASTNode source, FieldAccess fieldAccess) {
String typeName = getTypeName(type);
char[] suffix = ")".toCharArray();
@@ -209,21 +209,25 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
}
for (EclipseNode field : fields) {
TypeReference fType = getFieldType(field, fieldAccess);
TypeReference fieldType = getFieldType(field, fieldAccess);
Expression fieldAccessor = createFieldAccessor(field, fieldAccess, source);
// The distinction between primitive and object will be useful if we ever add a 'hideNulls' option.
boolean fieldBaseTypeIsPrimitive = BUILT_IN_TYPES.contains(new String(fieldType.getLastToken()));
boolean fieldIsPrimitive = fieldType.dimensions() == 0 && fieldBaseTypeIsPrimitive;
boolean fieldIsPrimitiveArray = fieldType.dimensions() == 1 && fieldBaseTypeIsPrimitive;
boolean fieldIsObjectArray = fieldType.dimensions() > 0 && !fieldIsPrimitiveArray;
@SuppressWarnings("unused")
boolean fieldIsObject = !fieldIsPrimitive && !fieldIsPrimitiveArray && !fieldIsObjectArray;
Expression ex;
if (fType.dimensions() > 0) {
if (fieldIsPrimitiveArray || fieldIsObjectArray) {
MessageSend arrayToString = new MessageSend();
arrayToString.sourceStart = pS; arrayToString.sourceEnd = pE;
arrayToString.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA, TypeConstants.UTIL, "Arrays".toCharArray());
arrayToString.arguments = new Expression[] { fieldAccessor };
setGeneratedBy(arrayToString.arguments[0], source);
if (fType.dimensions() > 1 || !BUILT_IN_TYPES.contains(new String(fType.getLastToken()))) {
arrayToString.selector = "deepToString".toCharArray();
} else {
arrayToString.selector = "toString".toCharArray();
}
arrayToString.selector = (fieldIsObjectArray ? "deepToString" : "toString").toCharArray();
ex = arrayToString;
} else {
ex = fieldAccessor;
@@ -278,7 +282,7 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
return method;
}
private String getTypeName(EclipseNode type) {
private static String getTypeName(EclipseNode type) {
String typeName = getSingleTypeName(type);
EclipseNode upType = type.up();
while (upType.getKind() == Kind.TYPE) {
@@ -288,7 +292,7 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
return typeName;
}
private String getSingleTypeName(EclipseNode type) {
private static String getSingleTypeName(EclipseNode type) {
TypeDeclaration typeDeclaration = (TypeDeclaration)type.get();
char[] rawTypeName = typeDeclaration.name;
return rawTypeName == null ? "" : new String(rawTypeName);
@@ -297,7 +301,7 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
private static final Set<String> BUILT_IN_TYPES = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
"byte", "short", "int", "long", "char", "boolean", "double", "float")));
private NameReference generateQualifiedNameRef(ASTNode source, char[]... varNames) {
private static NameReference generateQualifiedNameRef(ASTNode source, char[]... varNames) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
NameReference ref;
@@ -30,8 +30,9 @@ import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
import lombok.eclipse.handlers.HandleConstructor.SkipIfConstructorExists;
import lombok.experimental.NonFinal;
import lombok.experimental.Value;
import lombok.Value;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
@@ -78,6 +79,6 @@ public class HandleValue extends EclipseAnnotationHandler<Value> {
new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode);
new HandleToString().generateToStringForType(typeNode, annotationNode);
new HandleConstructor().generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), true, Collections.<Annotation>emptyList(), ast);
new HandleConstructor().generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), SkipIfConstructorExists.YES, Collections.<Annotation>emptyList(), ast);
}
}
@@ -160,7 +160,7 @@ public class HandleWither extends EclipseAnnotationHandler<Wither> {
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
TypeReference fieldType = copyType(field.type, source);
boolean isBoolean = nameEquals(fieldType.getTypeName(), "boolean") && fieldType.dimensions() == 0;
boolean isBoolean = isBoolean(fieldType);
String witherName = toWitherName(fieldNode, isBoolean);
if (witherName == null) {
@@ -0,0 +1,147 @@
/*
* Copyright (C) 2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.eclipse.handlers;
import java.util.Arrays;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.IfStatement;
import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
import org.eclipse.jdt.internal.compiler.ast.OperatorIds;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.ThrowStatement;
import org.mangosdk.spi.ProviderFor;
import lombok.NonNull;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.eclipse.DeferUntilPostDiet;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
import static lombok.eclipse.Eclipse.*;
import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
@DeferUntilPostDiet
@ProviderFor(EclipseAnnotationHandler.class)
public class NonNullHandler extends EclipseAnnotationHandler<NonNull> {
@Override public void handle(AnnotationValues<NonNull> annotation, Annotation ast, EclipseNode annotationNode) {
if (annotationNode.up().getKind() == Kind.FIELD) {
// This is meaningless unless the field is used to generate a method (@Setter, @RequiredArgsConstructor, etc),
// but in that case those handlers will take care of it. However, we DO check if the annotation is applied to
// a primitive, because those handlers trigger on any annotation named @NonNull and we only want the warning
// behaviour on _OUR_ 'lombok.NonNull'.
try {
if (isPrimitive(((AbstractVariableDeclaration) annotationNode.up().get()).type)) {
annotationNode.addWarning("@NonNull is meaningless on a primitive.");
}
} catch (Exception ignore) {}
return;
}
if (annotationNode.up().getKind() != Kind.ARGUMENT) return;
Argument arg;
AbstractMethodDeclaration declaration;
try {
arg = (Argument) annotationNode.up().get();
declaration = (AbstractMethodDeclaration) annotationNode.up().up().get();
} catch (Exception e) {
return;
}
if (isGenerated(declaration)) return;
// Possibly, if 'declaration instanceof ConstructorDeclaration', fetch declaration.constructorCall, search it for any references to our parameter,
// and if they exist, create a new method in the class: 'private static <T> T lombok$nullCheck(T expr, String msg) {if (expr == null) throw NPE; return expr;}' and
// wrap all references to it in the super/this to a call to this method.
Statement nullCheck = generateNullCheck(arg, ast);
if (nullCheck == null) {
// @NonNull applied to a primitive. Kinda pointless. Let's generate a warning.
annotationNode.addWarning("@NonNull is meaningless on a primitive.");
return;
}
if (declaration.statements == null) {
declaration.statements = new Statement[] {nullCheck};
} else {
char[] expectedName = arg.name;
for (Statement stat : declaration.statements) {
char[] varNameOfNullCheck = returnVarNameIfNullCheck(stat);
if (varNameOfNullCheck == null) break;
if (Arrays.equals(expectedName, varNameOfNullCheck)) return;
}
Statement[] newStatements = new Statement[declaration.statements.length + 1];
int skipOver = 0;
for (Statement stat : declaration.statements) {
if (isGenerated(stat)) skipOver++;
else break;
}
System.arraycopy(declaration.statements, 0, newStatements, 0, skipOver);
System.arraycopy(declaration.statements, skipOver, newStatements, skipOver + 1, declaration.statements.length - skipOver);
newStatements[skipOver] = nullCheck;
declaration.statements = newStatements;
}
annotationNode.up().up().rebuild();
}
private char[] returnVarNameIfNullCheck(Statement stat) {
if (!(stat instanceof IfStatement)) return null;
/* Check that the if's statement is a throw statement, possibly in a block. */ {
Statement then = ((IfStatement) stat).thenStatement;
if (then instanceof Block) {
Statement[] blockStatements = ((Block) then).statements;
if (blockStatements == null || blockStatements.length == 0) return null;
then = blockStatements[0];
}
if (!(then instanceof ThrowStatement)) return null;
}
/* Check that the if's conditional is like 'x == null'. Return from this method (don't generate
a nullcheck) if 'x' is equal to our own variable's name: There's already a nullcheck here. */ {
Expression cond = ((IfStatement) stat).condition;
if (!(cond instanceof EqualExpression)) return null;
EqualExpression bin = (EqualExpression) cond;
int operatorId = ((bin.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT);
if (operatorId != OperatorIds.EQUAL_EQUAL) return null;
if (!(bin.left instanceof SingleNameReference)) return null;
if (!(bin.right instanceof NullLiteral)) return null;
return ((SingleNameReference) bin.left).token;
}
}
}
+137
View File
@@ -0,0 +1,137 @@
/*
* Copyright (C) 2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.experimental;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* The builder annotation creates a so-called 'builder' aspect to the class that is annotated or the class
* that contains a member which is annotated with {@code @Builder}.
* <p>
* If a member is annotated, it must be either a constructor or a static method. If a class is annotated,
* then a private constructor is generated with all fields as arguments
* (as if {@code @AllArgsConstructor(AccessLevel.PRIVATE)} is present
* on the class), and it is as if this constructor has been annotated with {@code @Builder} instead.
* <p>
* The effect of {@code @Builder} is that an inner class is generated named <code><strong>T</strong>Builder</code>,
* with a private constructor. Instances of <code><strong>T</strong>Builder</code> are made with the static
* method named {@code builder()} which is also generated for you in the class itself (not in the builder class).
* <p>
* The <code><strong>T</strong>Builder</code> class contains 1 method for each parameter of the annotated
* constructor / static method (each field, when annotating a class), which returns the builder itself.
* The builder also has a <code>build()</code> method which returns a completed instance of the original type,
* created by passing all parameters as set via the various other methods in the builder to the constructor
* or static method that was annotated with {@code @Builder}. The return type of this method will be the same
* as the relevant class, unless a static method has been annotated, in which case it'll be equal to the
* return type of that method.
* <p>
* Complete documentation is found at <a href="http://projectlombok.org/features/experimental/Builder.html">the project lombok features page for &#64;Builder</a>.
* <p>
* <p>
* Before:
*
* <pre>
* &#064;Builder
* class Example {
* private int foo;
* private final String bar;
* }
* </pre>
*
* After:
*
* <pre>
* class Example&lt;T&gt; {
* private T foo;
* private final String bar;
*
* private Example(T foo, String bar) {
* this.foo = foo;
* this.bar = bar;
* }
*
* public static &lt;T&gt; ExampleBuilder&lt;T&gt; builder() {
* return new ExampleBuilder&lt;T&gt;();
* }
*
* public static class ExampleBuilder&lt;T&gt; {
* private T foo;
* private String bar;
*
* private ExampleBuilder() {}
*
* public ExampleBuilder foo(T foo) {
* this.foo = foo;
* return this;
* }
*
* public ExampleBuilder bar(String bar) {
* this.bar = bar;
* return this;
* }
*
* &#064;java.lang.Override public String toString() {
* return "ExampleBuilder(foo = " + foo + ", bar = " + bar + ")";
* }
*
* public Example build() {
* return new Example(foo, bar);
* }
* }
* }
* </pre>
*/
@Target({TYPE, METHOD, CONSTRUCTOR})
@Retention(SOURCE)
public @interface Builder {
/** Name of the static method that creates a new builder instance. Default: {@code builder}. */
String builderMethodName() default "builder";
/** Name of the instance method in the builder class that creates an instance of your {@code @Builder}-annotated class. */
String buildMethodName() default "build";
/** Name of the builder class.
* Default for {@code @Builder} on types and constructors: {@code (TypeName)Builder}.
* Default for {@code @Builder} on static methods: {@code (ReturnTypeName)Builder}.
*/
String builderClassName() default "";
/**
* Normally the builder's 'set' methods are fluent, meaning, they have the same name as the field. Set this
* to {@code false} to name the setter method for field {@code someField}: {@code setSomeField}.
* <p>
* <strong>Default: true</strong>
*/
boolean fluent() default true;
/**
* Normally the builder's 'set' methods are chaining, meaning, they return the builder so that you can chain
* calls to set methods. Set this to {@code false} to have these 'set' methods return {@code void} instead.
* <p>
* <strong>Default: true</strong>
*/
boolean chain() default true;
}
+2
View File
@@ -39,9 +39,11 @@ import java.lang.annotation.Target;
* @see lombok.ToString
* @see lombok.EqualsAndHashCode
* @see lombok.Data
* @deprecated {@link lombok.Value} has been promoted to the main package, so use that one instead.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
@Deprecated
public @interface Value {
/**
* If you specify a static constructor name, then the generated constructor will be private, and
+2 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2012 The Project Lombok Authors.
* Copyright (C) 2010-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -52,6 +52,7 @@ import java.lang.annotation.Target;
* @see org.apache.commons.logging.LogFactory#getLog(java.lang.Class) org.apache.commons.logging.LogFactory.getLog(Class target)
* @see lombok.extern.java.Log &#64;Log
* @see lombok.extern.log4j.Log4j &#64;Log4j
* @see lombok.extern.log4j.Log4j2 &#64;Log4j2
* @see lombok.extern.slf4j.Slf4j &#64;Slf4j
* @see lombok.extern.slf4j.XSlf4j &#64;XSlf4j
*/
+2 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2012 The Project Lombok Authors.
* Copyright (C) 2010-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -51,6 +51,7 @@ import java.lang.annotation.Target;
* @see java.util.logging.Logger#getLogger(java.lang.String) java.util.logging.Logger.getLogger(String name)
* @see lombok.extern.apachecommons.CommonsLog &#64;CommonsLog
* @see lombok.extern.log4j.Log4j &#64;Log4j
* @see lombok.extern.log4j.Log4j2 &#64;Log4j2
* @see lombok.extern.slf4j.Slf4j &#64;Slf4j
* @see lombok.extern.slf4j.XSlf4j &#64;XSlf4j
*/
+2 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2012 The Project Lombok Authors.
* Copyright (C) 2010-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -50,6 +50,7 @@ import java.lang.annotation.Target;
*
* @see org.apache.log4j.Logger org.apache.log4j.Logger
* @see org.apache.log4j.Logger#getLogger(java.lang.Class) org.apache.log4j.Logger.getLogger(Class target)
* @see lombok.extern.log4j.Log4j2 &#64;Log4j2
* @see lombok.extern.apachecommons.CommonsLog &#64;CommonsLog
* @see lombok.extern.java.Log &#64;Log
* @see lombok.extern.slf4j.Slf4j &#64;Slf4j
+62
View File
@@ -0,0 +1,62 @@
/*
* Copyright (C) 2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.extern.log4j;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Causes lombok to generate a logger field.
* <p>
* Complete documentation is found at <a href="http://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>.
* <p>
* Example:
* <pre>
* &#64;Log4j2
* public class LogExample {
* }
* </pre>
*
* will generate:
*
* <pre>
* public class LogExample {
* private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.Logger.getLogger(LogExample.class);
* }
* </pre>
*
* This annotation is valid for classes and enumerations.<br />
*
* @see org.apache.logging.log4j.Logger org.apache.logging.log4j.Logger
* @see org.apache.logging.log4j.LogManager#getLogger(java.lang.Class) org.apache.logging.log4j.LogManager.getLogger(Class target)
* @see lombok.extern.log4j.Log4j &#64;Log4j
* @see lombok.extern.apachecommons.CommonsLog &#64;CommonsLog
* @see lombok.extern.java.Log &#64;Log
* @see lombok.extern.slf4j.Slf4j &#64;Slf4j
* @see lombok.extern.slf4j.XSlf4j &#64;XSlf4j
*/
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface Log4j2 {
}
+2 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2012 The Project Lombok Authors.
* Copyright (C) 2010-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -51,6 +51,7 @@ import java.lang.annotation.Target;
* @see lombok.extern.apachecommons.CommonsLog &#64;CommonsLog
* @see lombok.extern.java.Log &#64;Log
* @see lombok.extern.log4j.Log4j &#64;Log4j
* @see lombok.extern.log4j.Log4j2 &#64;Log4j2
*/
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
+2 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012 The Project Lombok Authors.
* Copyright (C) 2012-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -51,6 +51,7 @@ import java.lang.annotation.Target;
* @see lombok.extern.apachecommons.CommonsLog &#64;CommonsLog
* @see lombok.extern.java.Log &#64;Log
* @see lombok.extern.log4j.Log4j &#64;Log4j
* @see lombok.extern.log4j.Log4j2 &#64;Log4j2
* @see lombok.extern.slf4j.Slf4j &#64;Slf4j
*/
@Retention(RetentionPolicy.SOURCE)
@@ -88,7 +88,7 @@ public class FindTypeVarScanner extends AbstractTypeVisitor6<Void, Void> {
@Override public Void visitTypeVariable(TypeVariable t, Void p) {
Name name = null;
try {
name = ((Type)t).tsym.name;
name = ((Type) t).tsym.name;
} catch (NullPointerException e) {}
if (name != null) typeVariables.add(name.toString());
subVisit(t.getLowerBound());
+16 -16
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2012 The Project Lombok Authors.
* Copyright (C) 2009-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -231,23 +231,23 @@ public class HandlerLibrary {
* @param annotation 'node.get()' - convenience parameter.
*/
public void handleAnnotation(JCCompilationUnit unit, JavacNode node, JCAnnotation annotation, long priority) {
TypeResolver resolver = new TypeResolver(node.getPackageDeclaration(), node.getImportStatements());
TypeResolver resolver = new TypeResolver(node.getImportList());
String rawType = annotation.annotationType.toString();
for (String fqn : resolver.findTypeMatches(node, typeLibrary, rawType)) {
AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn);
if (container == null) continue;
try {
if (container.getPriority() == priority) {
if (checkAndSetHandled(annotation)) container.handle(node);
}
} catch (AnnotationValueDecodeFail fail) {
fail.owner.setError(fail.getMessage(), fail.idx);
} catch (Throwable t) {
String sourceName = "(unknown).java";
if (unit != null && unit.sourcefile != null) sourceName = unit.sourcefile.getName();
javacError(String.format("Lombok annotation handler %s failed on " + sourceName, container.handler.getClass()), t);
String fqn = resolver.typeRefToFullyQualifiedName(node, typeLibrary, rawType);
if (fqn == null) return;
AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn);
if (container == null) return;
try {
if (container.getPriority() == priority) {
if (checkAndSetHandled(annotation)) container.handle(node);
}
} catch (AnnotationValueDecodeFail fail) {
fail.owner.setError(fail.getMessage(), fail.idx);
} catch (Throwable t) {
String sourceName = "(unknown).java";
if (unit != null && unit.sourcefile != null) sourceName = unit.sourcefile.getName();
javacError(String.format("Lombok annotation handler %s failed on " + sourceName, container.handler.getClass()), t);
}
}
@@ -26,7 +26,6 @@ import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Options;
public class Javac6BasedLombokOptions extends LombokOptions {
public static Javac6BasedLombokOptions replaceWithDelombokOptions(Context context) {
Options options = Options.instance(context);
context.put(optionsKey, (Options)null);
@@ -38,7 +37,7 @@ public class Javac6BasedLombokOptions extends LombokOptions {
private Javac6BasedLombokOptions(Context context) {
super(context);
}
@Override public void putJavacOption(String optionName, String value) {
put(OptionName.valueOf(optionName), value);
}
+71 -18
View File
@@ -24,6 +24,7 @@ package lombok.javac;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.annotation.processing.Messager;
@@ -34,10 +35,12 @@ import javax.tools.JavaFileObject;
import lombok.core.AST;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Source;
import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.model.JavacTypes;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCCatch;
import com.sun.tools.javac.tree.JCTree.JCTry;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCBlock;
@@ -46,7 +49,6 @@ import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCImport;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
@@ -78,7 +80,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
* @param top The compilation unit, which serves as the top level node in the tree to be built.
*/
public JavacAST(Messager messager, Context context, JCCompilationUnit top) {
super(sourceName(top), packageDeclaration(top), imports(top));
super(sourceName(top), packageDeclaration(top), new JavacImportList(top));
setTop(buildCompilationUnit(top));
this.context = context;
this.messager = messager;
@@ -98,16 +100,6 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
return (cu.pid instanceof JCFieldAccess || cu.pid instanceof JCIdent) ? cu.pid.toString() : null;
}
private static Collection<String> imports(JCCompilationUnit cu) {
List<String> imports = new ArrayList<String>();
for (JCTree def : cu.defs) {
if (def instanceof JCImport) {
imports.add(((JCImport)def).qualid.toString());
}
}
return imports;
}
public Context getContext() {
return context;
}
@@ -121,9 +113,20 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
}
void traverseChildren(JavacASTVisitor visitor, JavacNode node) {
for (JavacNode child : new ArrayList<JavacNode>(node.down())) {
child.traverse(visitor);
}
for (JavacNode child : node.down()) child.traverse(visitor);
}
@Override public int getSourceVersion() {
try {
String nm = Source.instance(context).name();
int underscoreIdx = nm.indexOf('_');
if (underscoreIdx > -1) return Integer.parseInt(nm.substring(underscoreIdx + 1));
} catch (Exception ignore) {}
return 6;
}
@Override public int getLatestJavaSpecSupported() {
return Javac.getJavaCompilerVersion();
}
/** @return A Name object generated for the proper name table belonging to this AST. */
@@ -223,6 +226,46 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
return putInMap(new JavacNode(this, local, childNodes, kind));
}
private static boolean JCTRY_RESOURCES_FIELD_INITIALIZED;
private static Field JCTRY_RESOURCES_FIELD;
@SuppressWarnings("unchecked")
private static List<JCTree> getResourcesForTryNode(JCTry tryNode) {
if (!JCTRY_RESOURCES_FIELD_INITIALIZED) {
try {
JCTRY_RESOURCES_FIELD = JCTry.class.getField("resources");
} catch (NoSuchFieldException ignore) {
// Java 1.6 or lower won't have this at all.
} catch (Exception ignore) {
// Shouldn't happen. Best thing we can do is just carry on and break on try/catch.
}
JCTRY_RESOURCES_FIELD_INITIALIZED = true;
}
if (JCTRY_RESOURCES_FIELD == null) return Collections.emptyList();
Object rv = null;
try {
rv = JCTRY_RESOURCES_FIELD.get(tryNode);
} catch (Exception ignore) {}
if (rv instanceof List) return (List<JCTree>) rv;
return Collections.emptyList();
}
private JavacNode buildTry(JCTry tryNode) {
if (setAndGetAsHandled(tryNode)) return null;
List<JavacNode> childNodes = new ArrayList<JavacNode>();
for (JCTree varDecl : getResourcesForTryNode(tryNode)) {
if (varDecl instanceof JCVariableDecl) {
addIfNotNull(childNodes, buildLocalVar((JCVariableDecl) varDecl, Kind.LOCAL));
}
}
addIfNotNull(childNodes, buildStatement(tryNode.body));
for (JCCatch jcc : tryNode.catchers) addIfNotNull(childNodes, buildTree(jcc, Kind.STATEMENT));
addIfNotNull(childNodes, buildStatement(tryNode.finalizer));
return putInMap(new JavacNode(this, tryNode, childNodes, Kind.STATEMENT));
}
private JavacNode buildInitializer(JCBlock initializer) {
if (setAndGetAsHandled(initializer)) return null;
List<JavacNode> childNodes = new ArrayList<JavacNode>();
@@ -264,6 +307,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
if (statement instanceof JCAnnotation) return null;
if (statement instanceof JCClassDecl) return buildType((JCClassDecl)statement);
if (statement instanceof JCVariableDecl) return buildLocalVar((JCVariableDecl)statement, Kind.LOCAL);
if (statement instanceof JCTry) return buildTry((JCTry) statement);
if (setAndGetAsHandled(statement)) return null;
@@ -271,9 +315,18 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
}
private JavacNode drill(JCTree statement) {
List<JavacNode> childNodes = new ArrayList<JavacNode>();
for (FieldAccess fa : fieldsOf(statement.getClass())) childNodes.addAll(buildWithField(JavacNode.class, statement, fa));
return putInMap(new JavacNode(this, statement, childNodes, Kind.STATEMENT));
try {
List<JavacNode> childNodes = new ArrayList<JavacNode>();
for (FieldAccess fa : fieldsOf(statement.getClass())) childNodes.addAll(buildWithField(JavacNode.class, statement, fa));
return putInMap(new JavacNode(this, statement, childNodes, Kind.STATEMENT));
} catch (OutOfMemoryError oome) {
String msg = oome.getMessage();
if (msg == null) msg = "(no original message)";
OutOfMemoryError newError = new OutOfMemoryError(getFileName() + "@pos" + statement.getPreferredPosition() + ": " + msg);
// We could try to set the stack trace of the new exception to the same one as the old exception, but this costs memory,
// and we're already in an extremely fragile situation in regards to remaining heap space, so let's not do that.
throw newError;
}
}
/** For javac, both JCExpression and JCStatement are considered as valid children types. */
@@ -33,7 +33,7 @@ import com.sun.tools.javac.tree.JCTree.JCAnnotation;
*
* You MUST replace 'T' with a specific annotation type, such as:
*
* {@code public class HandleGetter implements JavacAnnotationHandler<Getter>}
* {@code public class HandleGetter extends JavacAnnotationHandler<Getter>}
*
* Because this generics parameter is inspected to figure out which class you're interested in.
*
+112
View File
@@ -0,0 +1,112 @@
/*
* Copyright (C) 2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.javac;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCImport;
import com.sun.tools.javac.util.List;
import lombok.core.ImportList;
import lombok.core.LombokInternalAliasing;
public class JavacImportList implements ImportList {
private final JCExpression pkg;
private final List<JCTree> defs;
public JavacImportList(JCCompilationUnit cud) {
this.pkg = cud.pid;
this.defs = cud.defs;
}
@Override public String getFullyQualifiedNameForSimpleName(String unqualified) {
for (JCTree def : defs) {
if (!(def instanceof JCImport)) continue;
JCTree qual = ((JCImport) def).qualid;
if (!(qual instanceof JCFieldAccess)) continue;
String simpleName = ((JCFieldAccess) qual).name.toString();
if (simpleName.equals(unqualified)) {
return LombokInternalAliasing.processAliases(qual.toString());
}
}
return null;
}
@Override public boolean hasStarImport(String packageName) {
for (Map.Entry<String, String> e : LombokInternalAliasing.IMPLIED_EXTRA_STAR_IMPORTS.entrySet()) {
if (e.getValue().equals(packageName) && hasStarImport(e.getKey())) return true;
}
if (pkg != null && pkg.toString().equals(packageName)) return true;
if ("java.lang".equals(packageName)) return true;
for (JCTree def : defs) {
if (!(def instanceof JCImport)) continue;
if (((JCImport) def).staticImport) continue;
JCTree qual = ((JCImport) def).qualid;
if (!(qual instanceof JCFieldAccess)) continue;
String simpleName = ((JCFieldAccess) qual).name.toString();
if (!"*".equals(simpleName)) continue;
if (packageName.equals(((JCFieldAccess) qual).selected.toString())) return true;
}
return false;
}
@Override public Collection<String> applyNameToStarImports(String startsWith, String name) {
ArrayList<String> out = new ArrayList<String>();
if (pkg != null && topLevelName(pkg).equals(startsWith)) out.add(pkg.toString() + "." + name);
for (JCTree def : defs) {
if (!(def instanceof JCImport)) continue;
if (((JCImport) def).staticImport) continue;
JCTree qual = ((JCImport) def).qualid;
if (!(qual instanceof JCFieldAccess)) continue;
String simpleName = ((JCFieldAccess) qual).name.toString();
if (!"*".equals(simpleName)) continue;
String topLevelName = topLevelName(qual);
if (topLevelName.equals(startsWith)) {
out.add(((JCFieldAccess) qual).selected.toString() + "." + name);
}
}
return out;
}
private String topLevelName(JCTree tree) {
while (tree instanceof JCFieldAccess) tree = ((JCFieldAccess) tree).selected;
return tree.toString();
}
@Override public String applyUnqualifiedNameToPackage(String unqualified) {
if (pkg == null) return unqualified;
return pkg.toString() + "." + unqualified;
}
}
+2 -2
View File
@@ -433,8 +433,8 @@ public class JavacResolution {
if (symbol.name.length() == 0) {
// Anonymous inner class
if (type instanceof ClassType) {
List<Type> ifaces = ((ClassType)type).interfaces_field;
Type supertype = ((ClassType)type).supertype_field;
List<Type> ifaces = ((ClassType) type).interfaces_field;
Type supertype = ((ClassType) type).supertype_field;
if (ifaces != null && ifaces.length() == 1) {
return typeToJCTree(ifaces.get(0), ast, allowCompound, allowVoid);
}
+2 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010 The Project Lombok Authors.
* Copyright (C) 2010-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -49,6 +49,6 @@ public abstract class LombokOptions extends Options {
protected LombokOptions(Context context) {
super(context);
}
public abstract void putJavacOption(String optionName, String value);
}
-4
View File
@@ -62,10 +62,6 @@ import com.sun.tools.javac.util.Context;
/**
* This Annotation Processor is the standard injection mechanism for lombok-enabling the javac compiler.
*
* Due to lots of changes in the core javac code, as well as lombok's heavy usage of non-public API, this
* code only works for the javac v1.6 compiler; it definitely won't work for javac v1.5, and it probably
* won't work for javac v1.7 without modifications.
*
* To actually enable lombok in a javac compilation run, this class should be in the classpath when
* running javac; that's the only requirement.
*/
@@ -0,0 +1,330 @@
/*
* Copyright (C) 2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.javac.handlers;
import java.util.ArrayList;
import java.util.Collections;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCTypeApply;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;
import lombok.AccessLevel;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.core.TransformationsUtil;
import lombok.experimental.Builder;
import lombok.experimental.NonFinal;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.handlers.HandleConstructor.SkipIfConstructorExists;
import static lombok.javac.Javac.*;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.javac.handlers.JavacHandlerUtil.*;
@ProviderFor(JavacAnnotationHandler.class)
@HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes.
public class HandleBuilder extends JavacAnnotationHandler<Builder> {
@Override public void handle(AnnotationValues<Builder> annotation, JCAnnotation ast, JavacNode annotationNode) {
Builder builderInstance = annotation.getInstance();
String builderMethodName = builderInstance.builderMethodName();
String buildMethodName = builderInstance.buildMethodName();
String builderClassName = builderInstance.builderClassName();
if (builderMethodName == null) builderMethodName = "builder";
if (buildMethodName == null) buildMethodName = "build";
if (builderClassName == null) builderClassName = "";
if (!checkName("builderMethodName", builderMethodName, annotationNode)) return;
if (!checkName("buildMethodName", buildMethodName, annotationNode)) return;
if (!builderClassName.isEmpty()) {
if (!checkName("builderClassName", builderClassName, annotationNode)) return;
}
deleteAnnotationIfNeccessary(annotationNode, Builder.class);
deleteImportFromCompilationUnit(annotationNode, "lombok.experimental.Builder");
JavacNode parent = annotationNode.up();
java.util.List<JCExpression> typesOfParameters = new ArrayList<JCExpression>();
java.util.List<Name> namesOfParameters = new ArrayList<Name>();
JCExpression returnType;
List<JCTypeParameter> typeParams = List.nil();
List<JCExpression> thrownExceptions = List.nil();
Name nameOfStaticBuilderMethod;
JavacNode tdParent;
JCMethodDecl fillParametersFrom = parent.get() instanceof JCMethodDecl ? ((JCMethodDecl) parent.get()) : null;
if (parent.get() instanceof JCClassDecl) {
tdParent = parent;
JCClassDecl td = (JCClassDecl) tdParent.get();
ListBuffer<JavacNode> allFields = ListBuffer.lb();
@SuppressWarnings("deprecation")
boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation(lombok.experimental.Value.class, parent));
for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent)) {
JCVariableDecl fd = (JCVariableDecl) fieldNode.get();
// final fields with an initializer cannot be written to, so they can't be 'builderized'. Unfortunately presence of @Value makes
// non-final fields final, but @Value's handler hasn't done this yet, so we have to do this math ourselves.
// Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that.
if (fd.init != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue;
namesOfParameters.add(fd.name);
typesOfParameters.add(fd.vartype);
allFields.append(fieldNode);
}
new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, List.<JCAnnotation>nil(), allFields.toList(), null, SkipIfConstructorExists.I_AM_BUILDER, true, annotationNode);
returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
typeParams = td.typarams;
thrownExceptions = List.nil();
nameOfStaticBuilderMethod = null;
if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder";
} else if (fillParametersFrom != null && fillParametersFrom.getName().toString().equals("<init>")) {
if (!fillParametersFrom.typarams.isEmpty()) {
annotationNode.addError("@Builder is not supported on constructors with constructor type parameters.");
return;
}
tdParent = parent.up();
JCClassDecl td = (JCClassDecl) tdParent.get();
returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
typeParams = td.typarams;
thrownExceptions = fillParametersFrom.thrown;
nameOfStaticBuilderMethod = null;
if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder";
} else if (fillParametersFrom != null) {
tdParent = parent.up();
JCClassDecl td = (JCClassDecl) tdParent.get();
if ((fillParametersFrom.mods.flags & Flags.STATIC) == 0) {
annotationNode.addError("@Builder is only supported on types, constructors, and static methods.");
return;
}
returnType = fillParametersFrom.restype;
typeParams = fillParametersFrom.typarams;
thrownExceptions = fillParametersFrom.thrown;
nameOfStaticBuilderMethod = fillParametersFrom.name;
if (builderClassName.isEmpty()) {
if (returnType instanceof JCTypeApply) {
returnType = ((JCTypeApply) returnType).clazz;
}
if (returnType instanceof JCFieldAccess) {
builderClassName = ((JCFieldAccess) returnType).name.toString() + "Builder";
} else if (returnType instanceof JCIdent) {
Name n = ((JCIdent) returnType).name;
for (JCTypeParameter tp : typeParams) {
if (tp.name.equals(n)) {
annotationNode.addError("@Builder requires specifying 'builderClassName' if used on methods with a type parameter as return type.");
return;
}
}
builderClassName = n.toString() + "Builder";
} else if (returnType instanceof JCPrimitiveTypeTree) {
builderClassName = returnType.toString() + "Builder";
if (Character.isLowerCase(builderClassName.charAt(0))) {
builderClassName = Character.toTitleCase(builderClassName.charAt(0)) + builderClassName.substring(1);
}
} else {
// This shouldn't happen.
System.err.println("Lombok bug ID#20140614-1651: javac HandleBuilder: return type to name conversion failed: " + returnType.getClass());
builderClassName = td.name.toString() + "Builder";
}
}
} else {
annotationNode.addError("@Builder is only supported on types, constructors, and static methods.");
return;
}
if (fillParametersFrom != null) {
for (JCVariableDecl param : fillParametersFrom.params) {
namesOfParameters.add(param.name);
typesOfParameters.add(param.vartype);
}
}
JavacNode builderType = findInnerClass(tdParent, builderClassName);
if (builderType == null) {
builderType = makeBuilderClass(tdParent, builderClassName, typeParams, ast);
} else {
sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode);
}
java.util.List<JavacNode> fieldNodes = addFieldsToBuilder(builderType, namesOfParameters, typesOfParameters, ast);
java.util.List<JCMethodDecl> newMethods = new ArrayList<JCMethodDecl>();
for (JavacNode fieldNode : fieldNodes) {
JCMethodDecl newMethod = makeSetterMethodForBuilder(builderType, fieldNode, ast, builderInstance.fluent(), builderInstance.chain());
if (newMethod != null) newMethods.add(newMethod);
}
if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) {
JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, List.<JCAnnotation>nil(), builderType, List.<JavacNode>nil(), true, ast);
if (cd != null) injectMethod(builderType, cd);
}
for (JCMethodDecl newMethod : newMethods) injectMethod(builderType, newMethod);
if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) {
JCMethodDecl md = generateBuildMethod(buildMethodName, nameOfStaticBuilderMethod, returnType, namesOfParameters, builderType, thrownExceptions);
if (md != null) injectMethod(builderType, md);
}
if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
JCMethodDecl md = HandleToString.createToString(builderType, fieldNodes, true, false, FieldAccess.ALWAYS_FIELD, ast);
if (md != null) injectMethod(builderType, md);
}
if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
JCMethodDecl md = generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams);
if (md != null) injectMethod(tdParent, md);
}
}
private JCMethodDecl generateBuildMethod(String name, Name staticName, JCExpression returnType, java.util.List<Name> fieldNames, JavacNode type, List<JCExpression> thrownExceptions) {
TreeMaker maker = type.getTreeMaker();
JCExpression call;
JCStatement statement;
ListBuffer<JCExpression> args = ListBuffer.lb();
for (Name n : fieldNames) {
args.append(maker.Ident(n));
}
if (staticName == null) {
call = maker.NewClass(null, List.<JCExpression>nil(), returnType, args.toList(), null);
statement = maker.Return(call);
} else {
ListBuffer<JCExpression> typeParams = ListBuffer.lb();
for (JCTypeParameter tp : ((JCClassDecl) type.get()).typarams) {
typeParams.append(maker.Ident(tp.name));
}
JCExpression fn = maker.Select(maker.Ident(((JCClassDecl) type.up().get()).name), staticName);
call = maker.Apply(typeParams.toList(), fn, args.toList());
if (returnType instanceof JCPrimitiveTypeTree && compareCTC(getTypeTag((JCPrimitiveTypeTree) returnType), CTC_VOID)) {
statement = maker.Exec(call);
} else {
statement = maker.Return(call);
}
}
JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(name), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null);
}
private JCMethodDecl generateBuilderMethod(String builderMethodName, String builderClassName, JavacNode type, List<JCTypeParameter> typeParams) {
TreeMaker maker = type.getTreeMaker();
ListBuffer<JCExpression> typeArgs = ListBuffer.lb();
for (JCTypeParameter typeParam : typeParams) {
typeArgs.append(maker.Ident(typeParam.name));
}
JCExpression call = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.<JCExpression>nil(), null);
JCStatement statement = maker.Return(call);
JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
return maker.MethodDef(maker.Modifiers(Flags.STATIC | Flags.PUBLIC), type.toName(builderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), copyTypeParams(maker, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
}
private java.util.List<JavacNode> addFieldsToBuilder(JavacNode builderType, java.util.List<Name> namesOfParameters, java.util.List<JCExpression> typesOfParameters, JCTree source) {
int len = namesOfParameters.size();
java.util.List<JavacNode> existing = new ArrayList<JavacNode>();
for (JavacNode child : builderType.down()) {
if (child.getKind() == Kind.FIELD) existing.add(child);
}
java.util.List<JavacNode>out = new ArrayList<JavacNode>();
top:
for (int i = len - 1; i >= 0; i--) {
Name name = namesOfParameters.get(i);
for (JavacNode exists : existing) {
Name n = ((JCVariableDecl) exists.get()).name;
if (n.equals(name)) {
out.add(exists);
continue top;
}
}
TreeMaker maker = builderType.getTreeMaker();
JCModifiers mods = maker.Modifiers(Flags.PRIVATE);
JCVariableDecl newField = maker.VarDef(mods, name, cloneType(maker, typesOfParameters.get(i), source), null);
out.add(injectField(builderType, newField));
}
Collections.reverse(out);
return out;
}
private JCMethodDecl makeSetterMethodForBuilder(JavacNode builderType, JavacNode fieldNode, JCTree source, boolean fluent, boolean chain) {
Name fieldName = ((JCVariableDecl) fieldNode.get()).name;
for (JavacNode child : builderType.down()) {
if (child.getKind() != Kind.METHOD) continue;
Name existingName = ((JCMethodDecl) child.get()).name;
if (existingName.equals(fieldName)) return null;
}
boolean isBoolean = isBoolean(fieldNode);
String setterName = fluent ? fieldNode.getName() : TransformationsUtil.toSetterName(null, fieldNode.getName(), isBoolean);
TreeMaker maker = builderType.getTreeMaker();
return HandleSetter.createSetter(Flags.PUBLIC, fieldNode, maker, setterName, chain, source, List.<JCAnnotation>nil(), List.<JCAnnotation>nil());
}
private JavacNode findInnerClass(JavacNode parent, String name) {
for (JavacNode child : parent.down()) {
if (child.getKind() != Kind.TYPE) continue;
JCClassDecl td = (JCClassDecl) child.get();
if (td.name.contentEquals(name)) return child;
}
return null;
}
private JavacNode makeBuilderClass(JavacNode tdParent, String builderClassName, List<JCTypeParameter> typeParams, JCAnnotation ast) {
TreeMaker maker = tdParent.getTreeMaker();
JCModifiers mods = maker.Modifiers(Flags.PUBLIC | Flags.STATIC);
JCClassDecl builder = ClassDef(maker, mods, tdParent.toName(builderClassName), copyTypeParams(maker, typeParams), null, List.<JCExpression>nil(), List.<JCTree>nil());
return injectType(tdParent, builder);
}
}
@@ -32,7 +32,6 @@ import lombok.javac.JavacNode;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.TypeTags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCAssign;
@@ -29,6 +29,7 @@ import lombok.RequiredArgsConstructor;
import lombok.core.AnnotationValues;
import lombok.core.TransformationsUtil;
import lombok.core.AST.Kind;
import lombok.experimental.Builder;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
@@ -66,7 +67,7 @@ public class HandleConstructor {
String staticName = ann.staticName();
if (level == AccessLevel.NONE) return;
List<JavacNode> fields = List.nil();
new HandleConstructor().generateConstructor(typeNode, level, onConstructor, fields, staticName, false, false, annotationNode);
new HandleConstructor().generateConstructor(typeNode, level, onConstructor, fields, staticName, SkipIfConstructorExists.NO, false, annotationNode);
}
}
@@ -84,7 +85,7 @@ public class HandleConstructor {
@SuppressWarnings("deprecation")
boolean suppressConstructorProperties = ann.suppressConstructorProperties();
if (level == AccessLevel.NONE) return;
new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findRequiredFields(typeNode), staticName, false, suppressConstructorProperties, annotationNode);
new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findRequiredFields(typeNode), staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, annotationNode);
}
}
@@ -119,11 +120,11 @@ public class HandleConstructor {
@SuppressWarnings("deprecation")
boolean suppressConstructorProperties = ann.suppressConstructorProperties();
if (level == AccessLevel.NONE) return;
new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findAllFields(typeNode), staticName, false, suppressConstructorProperties, annotationNode);
new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findAllFields(typeNode), staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, annotationNode);
}
}
private static List<JavacNode> findAllFields(JavacNode typeNode) {
static List<JavacNode> findAllFields(JavacNode typeNode) {
ListBuffer<JavacNode> fields = ListBuffer.lb();
for (JavacNode child : typeNode.down()) {
if (child.getKind() != Kind.FIELD) continue;
@@ -154,25 +155,34 @@ public class HandleConstructor {
return true;
}
public void generateRequiredArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, boolean skipIfConstructorExists, JavacNode source) {
public void generateRequiredArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source) {
generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findRequiredFields(typeNode), staticName, skipIfConstructorExists, false, source);
}
public void generateAllArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, boolean skipIfConstructorExists, JavacNode source) {
public enum SkipIfConstructorExists {
YES, NO, I_AM_BUILDER;
}
public void generateAllArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source) {
generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findAllFields(typeNode), staticName, skipIfConstructorExists, false, source);
}
public void generateConstructor(JavacNode typeNode, AccessLevel level, List<JCAnnotation> onConstructor, List<JavacNode> fields, String staticName, boolean skipIfConstructorExists, boolean suppressConstructorProperties, JavacNode source) {
public void generateConstructor(JavacNode typeNode, AccessLevel level, List<JCAnnotation> onConstructor, List<JavacNode> fields, String staticName, SkipIfConstructorExists skipIfConstructorExists, boolean suppressConstructorProperties, JavacNode source) {
boolean staticConstrRequired = staticName != null && !staticName.equals("");
if (skipIfConstructorExists && constructorExists(typeNode) != MemberExistsResult.NOT_EXISTS) return;
if (skipIfConstructorExists) {
if (skipIfConstructorExists != SkipIfConstructorExists.NO && constructorExists(typeNode) != MemberExistsResult.NOT_EXISTS) return;
if (skipIfConstructorExists != SkipIfConstructorExists.NO) {
for (JavacNode child : typeNode.down()) {
if (child.getKind() == Kind.ANNOTATION) {
if (annotationTypeMatches(NoArgsConstructor.class, child) ||
boolean skipGeneration = annotationTypeMatches(NoArgsConstructor.class, child) ||
annotationTypeMatches(AllArgsConstructor.class, child) ||
annotationTypeMatches(RequiredArgsConstructor.class, child)) {
annotationTypeMatches(RequiredArgsConstructor.class, child);
if (!skipGeneration && skipIfConstructorExists == SkipIfConstructorExists.YES) {
skipGeneration = annotationTypeMatches(Builder.class, child);
}
if (skipGeneration) {
if (staticConstrRequired) {
// @Data has asked us to generate a constructor, but we're going to skip this instruction, as an explicit 'make a constructor' annotation
// will take care of it. However, @Data also wants a specific static name; this will be ignored; the appropriate way to do this is to use
@@ -207,7 +217,7 @@ public class HandleConstructor {
mods.annotations = mods.annotations.append(annotation);
}
private JCMethodDecl createConstructor(AccessLevel level, List<JCAnnotation> onConstructor, JavacNode typeNode, List<JavacNode> fields, boolean suppressConstructorProperties, JCTree source) {
static JCMethodDecl createConstructor(AccessLevel level, List<JCAnnotation> onConstructor, JavacNode typeNode, List<JavacNode> fields, boolean suppressConstructorProperties, JCTree source) {
TreeMaker maker = typeNode.getTreeMaker();
boolean isEnum = (((JCClassDecl) typeNode.get()).mods.flags & Flags.ENUM) != 0;
@@ -243,7 +253,7 @@ public class HandleConstructor {
null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(), maker.Block(0L, nullChecks.appendList(assigns).toList()), null), source);
}
private boolean isLocalType(JavacNode type) {
private static boolean isLocalType(JavacNode type) {
Kind kind = type.up().getKind();
if (kind == Kind.COMPILATION_UNIT) return false;
if (kind == Kind.TYPE) return isLocalType(type.up());
@@ -27,6 +27,7 @@ import lombok.Data;
import lombok.core.AnnotationValues;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.handlers.HandleConstructor.SkipIfConstructorExists;
import org.mangosdk.spi.ProviderFor;
@@ -50,7 +51,7 @@ public class HandleData extends JavacAnnotationHandler<Data> {
String staticConstructorName = annotation.getInstance().staticConstructor();
// TODO move this to the end OR move it to the top in eclipse.
new HandleConstructor().generateRequiredArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, true, annotationNode);
new HandleConstructor().generateRequiredArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, SkipIfConstructorExists.YES, annotationNode);
new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
new HandleSetter().generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode);
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2012 The Project Lombok Authors.
* Copyright (C) 2009-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -25,6 +25,7 @@ import static lombok.javac.Javac.*;
import static lombok.javac.handlers.JavacHandlerUtil.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import lombok.EqualsAndHashCode;
@@ -178,17 +179,26 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
boolean isFinal = (((JCClassDecl)typeNode.get()).mods.flags & Flags.FINAL) != 0;
boolean needsCanEqual = !isFinal || !isDirectDescendantOfObject;
java.util.List<MemberExistsResult> existsResults = new ArrayList<MemberExistsResult>();
existsResults.add(methodExists("equals", typeNode, 1));
existsResults.add(methodExists("hashCode", typeNode, 0));
existsResults.add(methodExists("canEqual", typeNode, 1));
switch (Collections.max(existsResults)) {
MemberExistsResult equalsExists = methodExists("equals", typeNode, 1);
MemberExistsResult hashCodeExists = methodExists("hashCode", typeNode, 0);
MemberExistsResult canEqualExists = methodExists("canEqual", typeNode, 1);
switch (Collections.max(Arrays.asList(equalsExists, hashCodeExists, canEqualExists))) {
case EXISTS_BY_LOMBOK:
return;
case EXISTS_BY_USER:
if (whineIfExists) {
String msg = String.format("Not generating equals%s: A method with one of those names already exists. (Either all or none of these methods will be generated).", needsCanEqual ? ", hashCode and canEquals" : " and hashCode");
source.addWarning(msg);
} else if (equalsExists == MemberExistsResult.NOT_EXISTS || hashCodeExists == MemberExistsResult.NOT_EXISTS) {
// This means equals OR hashCode exists and not both (or neither, but canEqual is there).
// Even though we should suppress the message about not generating these, this is such a weird and surprising situation we should ALWAYS generate a warning.
// The user code couldn't possibly (barring really weird subclassing shenanigans) be in a shippable state anyway; the implementations of these 3 methods are
// all inter-related and should be written by the same entity.
String msg = String.format("Not generating %s: One of equals, hashCode, and canEqual exists. " +
"You should either write all of these are none of these (in the latter case, lombok generates them).",
equalsExists == MemberExistsResult.NOT_EXISTS && hashCodeExists == MemberExistsResult.NOT_EXISTS ? "equals and hashCode" :
equalsExists == MemberExistsResult.NOT_EXISTS ? "equals" : "hashCode");
source.addWarning(msg);
}
return;
case NOT_EXISTS:
@@ -44,7 +44,7 @@ import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
* Handles the {@code lombok.FieldDefaults} annotation for eclipse.
*/
@ProviderFor(JavacAnnotationHandler.class)
@HandlerPriority(-512) //-2^9; to ensure @Setter and such pick up on messing with the fields' 'final' state, run earlier.
@HandlerPriority(-2048) //-2^11; to ensure @Value picks up on messing with the fields' 'final' state, run earlier.
public class HandleFieldDefaults extends JavacAnnotationHandler<FieldDefaults> {
public boolean generateFieldDefaultsForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean makeFinal, boolean checkForTypeLevelFieldDefaults) {
if (checkForTypeLevelFieldDefaults) {
@@ -108,6 +108,14 @@ public class HandleFieldDefaults extends JavacAnnotationHandler<FieldDefaults> {
return;
}
if (level == AccessLevel.PACKAGE) {
annotationNode.addError("Setting 'level' to PACKAGE does nothing. To force fields as package private, use the @PackagePrivate annotation on the field.");
}
if (!makeFinal && annotation.isExplicit("makeFinal")) {
annotationNode.addError("Setting 'makeFinal' to false does nothing. To force fields to be non-final, use the @NonFinal annotation on the field.");
}
if (node == null) return;
generateFieldDefaultsForType(node, annotationNode, level, makeFinal, false);
@@ -53,11 +53,9 @@ import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import com.sun.tools.javac.tree.JCTree.JCIf;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.JCTree.JCNewClass;
import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCSynchronized;
import com.sun.tools.javac.tree.JCTree.JCTypeApply;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.TreeMaker;
@@ -256,6 +254,7 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> {
if (toClearOfMarkers != null) recursiveSetGeneratedBy(toClearOfMarkers, null);
decl.mods.annotations = decl.mods.annotations.appendList(delegates);
copyJavadoc(field, decl, CopyJavadoc.GETTER);
return decl;
}
@@ -287,6 +286,7 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> {
}
private static final String AR = "java.util.concurrent.atomic.AtomicReference";
private static final String JLO = "java.lang.Object";
private static final List<JCExpression> NIL_EXPRESSION = List.nil();
private static final java.util.Map<Object, String> TYPE_MAP;
@@ -305,37 +305,50 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> {
private List<JCStatement> createLazyGetterBody(TreeMaker maker, JavacNode fieldNode, JCTree source) {
/*
java.util.concurrent.atomic.AtomicReference<ValueType> value = this.fieldName.get();
java.lang.Object value = this.fieldName.get();
if (value == null) {
synchronized (this.fieldName) {
value = this.fieldName.get();
if (value == null) {
final ValueType actualValue = new ValueType();
value = new java.util.concurrent.atomic.AtomicReference<ValueType>(actualValue);
if (value == null) {
final RawValueType actualValue = INITIALIZER_EXPRESSION;
[IF PRIMITIVE]
value = actualValue;
[ELSE]
value = actualValue == null ? this.fieldName : actualValue;
[END IF]
this.fieldName.set(value);
}
}
}
return value.get();
[IF PRIMITIVE]
return (BoxedValueType) value;
[ELSE]
return (BoxedValueType) (value == this.fieldName ? null : value);
[END IF]
*/
ListBuffer<JCStatement> statements = ListBuffer.lb();
JCVariableDecl field = (JCVariableDecl) fieldNode.get();
JCExpression copyOfRawFieldType = copyType(maker, field);
JCExpression copyOfBoxedFieldType = null;
field.type = null;
boolean isPrimitive = false;
if (field.vartype instanceof JCPrimitiveTypeTree) {
String boxed = TYPE_MAP.get(((JCPrimitiveTypeTree)field.vartype).typetag);
if (boxed != null) {
isPrimitive = true;
field.vartype = chainDotsString(fieldNode, boxed);
copyOfBoxedFieldType = chainDotsString(fieldNode, boxed);
}
}
if (copyOfBoxedFieldType == null) copyOfBoxedFieldType = copyType(maker, field);
Name valueName = fieldNode.toName("value");
Name actualValueName = fieldNode.toName("actualValue");
/* java.util.concurrent.atomic.AtomicReference<ValueType> value = this.fieldName.get();*/ {
JCTypeApply valueVarType = maker.TypeApply(chainDotsString(fieldNode, AR), List.of(copyType(maker, field)));
/* java.lang.Object value = this.fieldName.get();*/ {
JCExpression valueVarType = chainDotsString(fieldNode, JLO);
statements.append(maker.VarDef(maker.Modifiers(0), valueName, valueVarType, callGet(fieldNode, createFieldAccessor(maker, fieldNode, FieldAccess.ALWAYS_FIELD))));
}
@@ -350,15 +363,23 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> {
/* if (value == null) { */ {
ListBuffer<JCStatement> innerIfStatements = ListBuffer.lb();
/* ValueType actualValue = new ValueType(); */ {
/* final RawValueType actualValue = INITIALIZER_EXPRESSION; */ {
innerIfStatements.append(maker.VarDef(maker.Modifiers(Flags.FINAL), actualValueName, copyOfRawFieldType, field.init));
}
/* value = new java.util.concurrent.atomic.AtomicReference<ValueType>(actualValue);*/ {
JCTypeApply valueVarType = maker.TypeApply(chainDotsString(fieldNode, AR), List.of(copyType(maker, field)));
JCNewClass newInstance = maker.NewClass(null, NIL_EXPRESSION, valueVarType, List.<JCExpression>of(maker.Ident(actualValueName)), null);
JCStatement statement = maker.Exec(maker.Assign(maker.Ident(valueName), newInstance));
innerIfStatements.append(statement);
/* [IF primitive] value = actualValue; */ {
if (isPrimitive) {
JCStatement statement = maker.Exec(maker.Assign(maker.Ident(valueName), maker.Ident(actualValueName)));
innerIfStatements.append(statement);
}
}
/* [ELSE] value = actualValue == null ? this.fieldName : actualValue; */ {
if (!isPrimitive) {
JCExpression actualValueIsNull = Javac.makeBinary(maker, CTC_EQUAL, maker.Ident(actualValueName), Javac.makeLiteral(maker, CTC_BOT, null));
JCExpression thisDotFieldName = createFieldAccessor(maker, fieldNode, FieldAccess.ALWAYS_FIELD);
JCExpression ternary = maker.Conditional(actualValueIsNull, thisDotFieldName, maker.Ident(actualValueName));
JCStatement statement = maker.Exec(maker.Assign(maker.Ident(valueName), ternary));
innerIfStatements.append(statement);
}
}
/* this.fieldName.set(value); */ {
JCStatement statement = callSet(fieldNode, createFieldAccessor(maker, fieldNode, FieldAccess.ALWAYS_FIELD), maker.Ident(valueName));
@@ -377,15 +398,25 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> {
JCIf ifStatement = maker.If(isNull, maker.Block(0, List.<JCStatement>of(synchronizedStatement)), null);
statements.append(ifStatement);
}
/* return value.get(); */
statements.append(maker.Return(callGet(fieldNode, maker.Ident(valueName))));
/* [IF PRIMITIVE] return (BoxedValueType) value; */ {
if (isPrimitive) {
statements.append(maker.Return(maker.TypeCast(copyOfBoxedFieldType, maker.Ident(valueName))));
}
}
/* [ELSE] return (BoxedValueType) (value == this.fieldName ? null : value); */ {
if (!isPrimitive) {
JCExpression valueEqualsSelf = Javac.makeBinary(maker, CTC_EQUAL, maker.Ident(valueName), createFieldAccessor(maker, fieldNode, FieldAccess.ALWAYS_FIELD));
JCExpression ternary = maker.Conditional(valueEqualsSelf, Javac.makeLiteral(maker, CTC_BOT, null), maker.Ident(valueName));
JCExpression typeCast = maker.TypeCast(copyOfBoxedFieldType, maker.Parens(ternary));
statements.append(maker.Return(typeCast));
}
}
// update the field type and init last
/* private final java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<ValueType> fieldName = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<ValueType>>(); */ {
/* private final java.util.concurrent.atomic.AtomicReference<Object> fieldName = new java.util.concurrent.atomic.AtomicReference<Object>(); */ {
field.vartype = recursiveSetGeneratedBy(
maker.TypeApply(chainDotsString(fieldNode, AR), List.<JCExpression>of(maker.TypeApply(chainDotsString(fieldNode, AR), List.of(copyType(maker, field))))),
source);
maker.TypeApply(chainDotsString(fieldNode, AR), List.<JCExpression>of(chainDotsString(fieldNode, JLO))), source);
field.init = recursiveSetGeneratedBy(maker.NewClass(null, NIL_EXPRESSION, copyType(maker, field), NIL_EXPRESSION, null), source);
}
+15 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2012 The Project Lombok Authors.
* Copyright (C) 2010-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -82,7 +82,7 @@ public class HandleLog {
private static boolean createField(LoggingFramework framework, JavacNode typeNode, JCFieldAccess loggingType, JCTree source) {
TreeMaker maker = typeNode.getTreeMaker();
// private static final <loggerType> log = <factoryMethod>(<parameter>);
// private static final <loggerType> log = <factoryMethod>(<parameter>);
JCExpression loggerType = chainDotsString(typeNode, framework.getLoggerTypeName());
JCExpression factoryMethod = chainDotsString(typeNode, framework.getLoggerFactoryMethodName());
@@ -127,6 +127,16 @@ public class HandleLog {
}
}
/**
* Handles the {@link lombok.extern.log4j.Log4j2} annotation for javac.
*/
@ProviderFor(JavacAnnotationHandler.class)
public static class HandleLog4j2Log extends JavacAnnotationHandler<lombok.extern.log4j.Log4j2> {
@Override public void handle(AnnotationValues<lombok.extern.log4j.Log4j2> annotation, JCAnnotation ast, JavacNode annotationNode) {
processAnnotation(LoggingFramework.LOG4J2, annotation, annotationNode);
}
}
/**
* Handles the {@link lombok.extern.slf4j.Slf4j} annotation for javac.
*/
@@ -163,6 +173,9 @@ public class HandleLog {
// private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(TargetType.class);
LOG4J(lombok.extern.log4j.Log4j.class, "org.apache.log4j.Logger", "org.apache.log4j.Logger.getLogger"),
// private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(TargetType.class);
LOG4J2(lombok.extern.log4j.Log4j2.class, "org.apache.logging.log4j.Logger", "org.apache.logging.log4j.LogManager.getLogger"),
// private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TargetType.class);
SLF4J(lombok.extern.slf4j.Slf4j.class, "org.slf4j.Logger", "org.slf4j.LoggerFactory.getLogger"),
@@ -26,10 +26,6 @@ import static lombok.javac.handlers.JavacHandlerUtil.*;
import java.util.Collection;
import javax.lang.model.type.NoType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeVisitor;
import lombok.AccessLevel;
import lombok.Setter;
import lombok.core.AST.Kind;
@@ -43,7 +39,6 @@ import lombok.javac.handlers.JavacHandlerUtil.FieldAccess;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCAssign;
@@ -195,9 +190,13 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> {
injectMethod(fieldNode.up(), createdSetter);
}
private JCMethodDecl createSetter(long access, JavacNode field, TreeMaker treeMaker, JCTree source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
static JCMethodDecl createSetter(long access, JavacNode field, TreeMaker treeMaker, JCTree source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
String setterName = toSetterName(field);
boolean returnThis = shouldReturnThis(field);
return createSetter(access, field, treeMaker, setterName, returnThis, source, onMethod, onParam);
}
static JCMethodDecl createSetter(long access, JavacNode field, TreeMaker treeMaker, String setterName, boolean shouldReturnThis, JCTree source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
if (setterName == null) return null;
JCVariableDecl fieldDecl = (JCVariableDecl) field.get();
@@ -223,17 +222,17 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> {
}
JCExpression methodType = null;
if (returnThis) {
if (shouldReturnThis) {
methodType = cloneSelfType(field);
}
if (methodType == null) {
//WARNING: Do not use field.getSymbolTable().voidType - that field has gone through non-backwards compatible API changes within javac1.6.
methodType = treeMaker.Type(new JCNoType(CTC_VOID));
returnThis = false;
methodType = treeMaker.Type(Javac.createVoidType(treeMaker, CTC_VOID));
shouldReturnThis = false;
}
if (returnThis) {
if (shouldReturnThis) {
JCReturn returnStatement = treeMaker.Return(treeMaker.Ident(field.toName("this")));
statements.append(returnStatement);
}
@@ -249,26 +248,9 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> {
annsOnMethod = annsOnMethod.prepend(treeMaker.Annotation(chainDots(field, "java", "lang", "Deprecated"), List.<JCExpression>nil()));
}
return recursiveSetGeneratedBy(treeMaker.MethodDef(treeMaker.Modifiers(access, annsOnMethod), methodName, methodType,
JCMethodDecl decl = recursiveSetGeneratedBy(treeMaker.MethodDef(treeMaker.Modifiers(access, annsOnMethod), methodName, methodType,
methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source);
}
private static class JCNoType extends Type implements NoType {
public JCNoType(Object tag) {
//FIX
super(1, null);
}
@Override
public TypeKind getKind() {
if (Javac.compareCTC(tag, CTC_VOID)) return TypeKind.VOID;
if (Javac.compareCTC(tag, CTC_NONE)) return TypeKind.NONE;
throw new AssertionError("Unexpected tag: " + tag);
}
@Override
public <R, P> R accept(TypeVisitor<R, P> v, P p) {
return v.visitNoType(this, p);
}
copyJavadoc(field, decl, CopyJavadoc.SETTER);
return decl;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2011 The Project Lombok Authors.
* Copyright (C) 2009-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -36,8 +36,6 @@ import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCBlock;
@@ -84,13 +82,20 @@ public class HandleSneakyThrows extends JavacAnnotationHandler<SneakyThrows> {
return;
}
if (method.body == null) return;
if (method.body.stats.isEmpty()) return;
if (method.body == null || method.body.stats.isEmpty()) {
generateEmptyBlockWarning(methodNode, annotation, false);
return;
}
final JCStatement constructorCall = method.body.stats.get(0);
final boolean isConstructorCall = isConstructorCall(constructorCall);
List<JCStatement> contents = isConstructorCall ? method.body.stats.tail : method.body.stats;
if (contents == null || contents.isEmpty()) {
generateEmptyBlockWarning(methodNode, annotation, true);
return;
}
for (String exception : exceptions) {
contents = List.of(buildTryCatchBlock(methodNode, contents, exception, annotation.get()));
}
@@ -99,14 +104,14 @@ public class HandleSneakyThrows extends JavacAnnotationHandler<SneakyThrows> {
methodNode.rebuild();
}
private boolean isConstructorCall(final JCStatement supect) {
if (!(supect instanceof JCExpressionStatement)) return false;
final JCExpression supectExpression = ((JCExpressionStatement) supect).expr;
if (!(supectExpression instanceof JCMethodInvocation)) return false;
final String methodName = ((JCMethodInvocation) supectExpression).meth.toString();
return "super".equals(methodName) || "this".equals(methodName);
private void generateEmptyBlockWarning(JavacNode methodNode, JavacNode annotation, boolean hasConstructorCall) {
if (hasConstructorCall) {
annotation.addWarning("Calls to sibling / super constructors are always excluded from @SneakyThrows; @SneakyThrows has been ignored because there is no other code in this constructor.");
} else {
annotation.addWarning("This method or constructor is empty; @SneakyThrows has been ignored.");
}
}
private JCStatement buildTryCatchBlock(JavacNode node, List<JCStatement> contents, String exception, JCTree source) {
TreeMaker maker = node.getTreeMaker();
@@ -24,6 +24,8 @@ package lombok.javac.handlers;
import static lombok.javac.handlers.JavacHandlerUtil.*;
import static lombok.javac.Javac.*;
import java.util.Collection;
import lombok.ToString;
import lombok.core.AnnotationValues;
import lombok.core.AST.Kind;
@@ -165,7 +167,7 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
}
}
private JCMethodDecl createToString(JavacNode typeNode, List<JavacNode> fields, boolean includeFieldNames, boolean callSuper, FieldAccess fieldAccess, JCTree source) {
static JCMethodDecl createToString(JavacNode typeNode, Collection<JavacNode> fields, boolean includeFieldNames, boolean callSuper, FieldAccess fieldAccess, JCTree source) {
TreeMaker maker = typeNode.getTreeMaker();
JCAnnotation overrideAnnotation = maker.Annotation(chainDots(typeNode, "java", "lang", "Override"), List.<JCExpression>nil());
@@ -199,18 +201,22 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
}
for (JavacNode fieldNode : fields) {
JCVariableDecl field = (JCVariableDecl) fieldNode.get();
JCExpression expr;
JCExpression fieldAccessor = createFieldAccessor(maker, fieldNode, fieldAccess);
if (getFieldType(fieldNode, fieldAccess) instanceof JCArrayTypeTree) {
boolean multiDim = ((JCArrayTypeTree)field.vartype).elemtype instanceof JCArrayTypeTree;
boolean primitiveArray = ((JCArrayTypeTree)field.vartype).elemtype instanceof JCPrimitiveTypeTree;
boolean useDeepTS = multiDim || !primitiveArray;
JCExpression hcMethod = chainDots(typeNode, "java", "util", "Arrays", useDeepTS ? "deepToString" : "toString");
expr = maker.Apply(List.<JCExpression>nil(), hcMethod, List.<JCExpression>of(fieldAccessor));
JCExpression fieldType = getFieldType(fieldNode, fieldAccess);
// The distinction between primitive and object will be useful if we ever add a 'hideNulls' option.
boolean fieldIsPrimitive = fieldType instanceof JCPrimitiveTypeTree;
boolean fieldIsPrimitiveArray = fieldType instanceof JCArrayTypeTree && ((JCArrayTypeTree) fieldType).elemtype instanceof JCPrimitiveTypeTree;
boolean fieldIsObjectArray = !fieldIsPrimitiveArray && fieldType instanceof JCArrayTypeTree;
@SuppressWarnings("unused")
boolean fieldIsObject = !fieldIsPrimitive && !fieldIsPrimitiveArray && !fieldIsObjectArray;
if (fieldIsPrimitiveArray || fieldIsObjectArray) {
JCExpression tsMethod = chainDots(typeNode, "java", "util", "Arrays", fieldIsObjectArray ? "deepToString" : "toString");
expr = maker.Apply(List.<JCExpression>nil(), tsMethod, List.<JCExpression>of(fieldAccessor));
} else expr = fieldAccessor;
if (first) {
@@ -238,7 +244,7 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null), source);
}
private String getTypeName(JavacNode typeNode) {
private static String getTypeName(JavacNode typeNode) {
String typeName = ((JCClassDecl) typeNode.get()).name.toString();
JavacNode upType = typeNode.up();
while (upType.getKind() == Kind.TYPE) {
@@ -22,13 +22,17 @@
package lombok.javac.handlers;
import static lombok.javac.handlers.JavacHandlerUtil.*;
import java.lang.annotation.Annotation;
import lombok.AccessLevel;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.experimental.NonFinal;
import lombok.experimental.Value;
import lombok.Value;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.handlers.HandleConstructor.SkipIfConstructorExists;
import org.mangosdk.spi.ProviderFor;
@@ -44,7 +48,9 @@ import com.sun.tools.javac.tree.JCTree.JCModifiers;
@HandlerPriority(-512) //-2^9; to ensure @EqualsAndHashCode and such pick up on this handler making the class final and messing with the fields' access levels, run earlier.
public class HandleValue extends JavacAnnotationHandler<Value> {
@Override public void handle(AnnotationValues<Value> annotation, JCAnnotation ast, JavacNode annotationNode) {
deleteAnnotationIfNeccessary(annotationNode, Value.class);
@SuppressWarnings("deprecation")
Class<? extends Annotation> oldExperimentalValue = lombok.experimental.Value.class;
deleteAnnotationIfNeccessary(annotationNode, Value.class, oldExperimentalValue);
JavacNode typeNode = annotationNode.up();
boolean notAClass = !isClass(typeNode);
@@ -65,7 +71,7 @@ public class HandleValue extends JavacAnnotationHandler<Value> {
new HandleFieldDefaults().generateFieldDefaultsForType(typeNode, annotationNode, AccessLevel.PRIVATE, true, true);
// TODO move this to the end OR move it to the top in eclipse.
new HandleConstructor().generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, true, annotationNode);
new HandleConstructor().generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, SkipIfConstructorExists.YES, annotationNode);
new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode);
new HandleToString().generateToStringForType(typeNode, annotationNode);
@@ -34,6 +34,7 @@ import lombok.experimental.Wither;
import lombok.javac.Javac;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.handlers.JavacHandlerUtil.CopyJavadoc;
import lombok.javac.handlers.JavacHandlerUtil.FieldAccess;
import org.mangosdk.spi.ProviderFor;
@@ -265,7 +266,9 @@ public class HandleWither extends JavacAnnotationHandler<Wither> {
if (isFieldDeprecated(field)) {
annsOnMethod = annsOnMethod.prepend(treeMaker.Annotation(chainDots(field, "java", "lang", "Deprecated"), List.<JCExpression>nil()));
}
return recursiveSetGeneratedBy(treeMaker.MethodDef(treeMaker.Modifiers(access, annsOnMethod), methodName, returnType,
JCMethodDecl decl = recursiveSetGeneratedBy(treeMaker.MethodDef(treeMaker.Modifiers(access, annsOnMethod), methodName, returnType,
methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source);
copyJavadoc(field, decl, CopyJavadoc.WITHER);
return decl;
}
}
@@ -21,6 +21,7 @@
*/
package lombok.javac.handlers;
import static lombok.core.TransformationsUtil.INVALID_ON_BUILDERS;
import static lombok.javac.Javac.*;
import java.lang.annotation.Annotation;
@@ -31,6 +32,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.AccessLevel;
@@ -51,9 +53,11 @@ import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
import com.sun.tools.javac.tree.JCTree.JCAssign;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCImport;
@@ -122,6 +126,7 @@ public class JavacHandlerUtil {
}
public static <T extends JCTree> T recursiveSetGeneratedBy(T node, JCTree source) {
if (node == null) return null;
setGeneratedBy(node, source);
node.accept(new MarkingScanner(source));
@@ -186,7 +191,7 @@ public class JavacHandlerUtil {
public static boolean typeMatches(Class<?> type, JavacNode node, JCTree typeNode) {
String typeName = typeNode.toString();
TypeResolver resolver = new TypeResolver(node.getPackageDeclaration(), node.getImportStatements());
TypeResolver resolver = new TypeResolver(node.getImportList());
return resolver.typeMatches(node, type.getName(), typeName);
}
@@ -278,7 +283,22 @@ public class JavacHandlerUtil {
* then removes any import statement that imports this exact annotation (not star imports).
* Only does this if the DeleteLombokAnnotations class is in the context.
*/
@SuppressWarnings("unchecked")
public static void deleteAnnotationIfNeccessary(JavacNode annotation, Class<? extends Annotation> annotationType) {
deleteAnnotationIfNeccessary0(annotation, annotationType);
}
/**
* Removes the annotation from javac's AST (it remains in lombok's AST),
* then removes any import statement that imports this exact annotation (not star imports).
* Only does this if the DeleteLombokAnnotations class is in the context.
*/
@SuppressWarnings("unchecked")
public static void deleteAnnotationIfNeccessary(JavacNode annotation, Class<? extends Annotation> annotationType1, Class<? extends Annotation> annotationType2) {
deleteAnnotationIfNeccessary0(annotation, annotationType1, annotationType2);
}
private static void deleteAnnotationIfNeccessary0(JavacNode annotation, Class<? extends Annotation>... annotationTypes) {
if (inNetbeansEditor(annotation)) return;
if (!annotation.shouldDeleteLombokAnnotations()) return;
JavacNode parentNode = annotation.directUp();
@@ -306,7 +326,10 @@ public class JavacHandlerUtil {
return;
}
deleteImportFromCompilationUnit(annotation, annotationType.getName());
parentNode.getAst().setChanged();
for (Class<?> annotationType : annotationTypes) {
deleteImportFromCompilationUnit(annotation, annotationType.getName());
}
}
public static void deleteImportFromCompilationUnit(JavacNode node, String name) {
@@ -425,8 +448,12 @@ public class JavacHandlerUtil {
}
}
private static boolean isBoolean(JavacNode field) {
public static boolean isBoolean(JavacNode field) {
JCExpression varType = ((JCVariableDecl) field.get()).vartype;
return isBoolean(varType);
}
public static boolean isBoolean(JCExpression varType) {
return varType != null && varType.toString().equals("boolean");
}
@@ -543,6 +570,23 @@ public class JavacHandlerUtil {
return MemberExistsResult.NOT_EXISTS;
}
public static boolean isConstructorCall(final JCStatement statement) {
if (!(statement instanceof JCExpressionStatement)) return false;
JCExpression expr = ((JCExpressionStatement) statement).expr;
if (!(expr instanceof JCMethodInvocation)) return false;
JCExpression invocation = ((JCMethodInvocation) expr).meth;
String name;
if (invocation instanceof JCFieldAccess) {
name = ((JCFieldAccess) invocation).name.toString();
} else if (invocation instanceof JCIdent) {
name = ((JCIdent) invocation).name.toString();
} else {
name = "";
}
return "super".equals(name) || "this".equals(name);
}
/**
* Turns an {@code AccessLevel} instance into the flag bit used by javac.
*/
@@ -710,11 +754,11 @@ public class JavacHandlerUtil {
*
* Also takes care of updating the JavacAST.
*/
public static void injectField(JavacNode typeNode, JCVariableDecl field) {
injectField(typeNode, field, false);
public static JavacNode injectField(JavacNode typeNode, JCVariableDecl field) {
return injectField(typeNode, field, false);
}
private static void injectField(JavacNode typeNode, JCVariableDecl field, boolean addSuppressWarnings) {
private static JavacNode injectField(JavacNode typeNode, JCVariableDecl field, boolean addSuppressWarnings) {
JCClassDecl type = (JCClassDecl) typeNode.get();
if (addSuppressWarnings) addSuppressWarningsAll(field.mods, typeNode, field.pos, getGeneratedBy(field));
@@ -740,7 +784,7 @@ public class JavacHandlerUtil {
insertAfter.tail = fieldEntry;
}
typeNode.add(field, Kind.FIELD);
return typeNode.add(field, Kind.FIELD);
}
private static boolean isEnumConstant(final JCVariableDecl field) {
@@ -781,6 +825,20 @@ public class JavacHandlerUtil {
typeNode.add(method, Kind.METHOD);
}
/**
* Adds an inner type (class, interface, enum) to the given type. Cannot inject top-level types.
*
* @param typeNode parent type to inject new type into
* @param type New type (class, interface, etc) to inject.
* @return
*/
public static JavacNode injectType(final JavacNode typeNode, final JCClassDecl type) {
JCClassDecl typeDecl = (JCClassDecl) typeNode.get();
addSuppressWarningsAll(type.mods, typeNode, type.pos, getGeneratedBy(type));
typeDecl.defs = typeDecl.defs.append(type);
return typeNode.add(type, Kind.TYPE);
}
private static void addSuppressWarningsAll(JCModifiers mods, JavacNode node, int pos, JCTree source) {
TreeMaker maker = node.getTreeMaker();
JCExpression suppressWarningsType = chainDots(node, "java", "lang", "SuppressWarnings");
@@ -890,7 +948,8 @@ public class JavacHandlerUtil {
JCExpression npe = chainDots(variable, "java", "lang", "NullPointerException");
JCTree exception = maker.NewClass(null, List.<JCExpression>nil(), npe, List.<JCExpression>of(maker.Literal(fieldName.toString())), null);
JCStatement throwStatement = maker.Throw(exception);
return maker.If(Javac.makeBinary(maker, CTC_EQUAL, maker.Ident(fieldName), Javac.makeLiteral(maker, CTC_BOT, null)), throwStatement, null);
JCBlock throwBlock = maker.Block(0, List.of(throwStatement));
return maker.If(Javac.makeBinary(maker, CTC_EQUAL, maker.Ident(fieldName), Javac.makeLiteral(maker, CTC_BOT, null)), throwBlock, null);
}
/**
@@ -991,6 +1050,49 @@ public class JavacHandlerUtil {
return result.toList();
}
public static List<JCTypeParameter> copyTypeParams(TreeMaker maker, List<JCTypeParameter> params) {
if (params == null || params.isEmpty()) return params;
ListBuffer<JCTypeParameter> out = ListBuffer.lb();
for (JCTypeParameter tp : params) out.append(maker.TypeParameter(tp.name, tp.bounds));
return out.toList();
}
public static JCExpression namePlusTypeParamsToTypeReference(TreeMaker maker, Name typeName, List<JCTypeParameter> params) {
ListBuffer<JCExpression> typeArgs = ListBuffer.lb();
if (!params.isEmpty()) {
for (JCTypeParameter param : params) {
typeArgs.append(maker.Ident(param.name));
}
return maker.TypeApply(maker.Ident(typeName), typeArgs.toList());
}
return maker.Ident(typeName);
}
public static void sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(JavacNode typeNode, JavacNode errorNode) {
List<String> disallowed = List.nil();
for (JavacNode child : typeNode.down()) {
for (Class<? extends java.lang.annotation.Annotation> annType : INVALID_ON_BUILDERS) {
if (annotationTypeMatches(annType, child)) {
disallowed = disallowed.append(annType.getSimpleName());
}
}
}
int size = disallowed.size();
if (size == 0) return;
if (size == 1) {
errorNode.addError("@" + disallowed.head + " is not allowed on builder classes.");
return;
}
StringBuilder out = new StringBuilder();
for (String a : disallowed) out.append("@").append(a).append(", ");
out.setLength(out.length() - 2);
errorNode.addError(out.append(" are not allowed on builder classes.").toString());
}
static List<JCAnnotation> copyAnnotations(List<? extends JCExpression> in) {
ListBuffer<JCAnnotation> out = ListBuffer.lb();
for (JCExpression expr : in) {
@@ -1090,4 +1192,103 @@ public class JavacHandlerUtil {
// This is somewhat unsafe, but it's better than outright throwing an exception here. Returning null will just cause an exception down the pipeline.
return (JCExpression) in;
}
private static final Pattern SECTION_FINDER = Pattern.compile("^\\s*\\**\\s*[-*][-*]+\\s*([GS]ETTER|WITHER)\\s*[-*][-*]+\\s*\\**\\s*$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);
private static String stripLinesWithTagFromJavadoc(String javadoc, String regexpFragment) {
Pattern p = Pattern.compile("^\\s*\\**\\s*" + regexpFragment + "\\s*\\**\\s*$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(javadoc);
return m.replaceAll("");
}
private static String[] splitJavadocOnSectionIfPresent(String javadoc, String sectionName) {
Matcher m = SECTION_FINDER.matcher(javadoc);
int getterSectionHeaderStart = -1;
int getterSectionStart = -1;
int getterSectionEnd = -1;
while (m.find()) {
if (m.group(1).equalsIgnoreCase(sectionName)) {
getterSectionStart = m.end() + 1;
getterSectionHeaderStart = m.start();
} else if (getterSectionStart != -1) {
getterSectionEnd = m.start();
}
}
if (getterSectionStart != -1) {
if (getterSectionEnd != -1) {
return new String[] {javadoc.substring(getterSectionStart, getterSectionEnd), javadoc.substring(0, getterSectionHeaderStart) + javadoc.substring(getterSectionEnd)};
} else {
return new String[] {javadoc.substring(getterSectionStart), javadoc.substring(0, getterSectionHeaderStart)};
}
}
return null;
}
public static enum CopyJavadoc {
VERBATIM, GETTER {
@Override public String[] split(String javadoc) {
// step 1: Check if there is a 'GETTER' section. If yes, that becomes the new method's javadoc and we strip that from the original.
String[] out = splitJavadocOnSectionIfPresent(javadoc, "GETTER");
if (out != null) return out;
// failing that, create a copy, but strip @return from the original and @param from the copy.
String copy = javadoc;
javadoc = stripLinesWithTagFromJavadoc(javadoc, "@returns?\\s+.*");
copy = stripLinesWithTagFromJavadoc(copy, "@param(?:eter)?\\s+.*");
return new String[] {copy, javadoc};
}
},
SETTER {
@Override public String[] split(String javadoc) {
return splitForSetters(javadoc, "SETTER");
}
},
WITHER {
@Override public String[] split(String javadoc) {
return splitForSetters(javadoc, "WITHER");
}
};
private static String[] splitForSetters(String javadoc, String sectionName) {
// step 1: Check if there is a 'SETTER' section. If yes, that becomes the new one and we strip that from the original.
String[] out = splitJavadocOnSectionIfPresent(javadoc, sectionName);
if (out != null) return out;
// failing that, create a copy, but strip @param from the original and @return from the copy.
String copy = javadoc;
javadoc = stripLinesWithTagFromJavadoc(javadoc, "@param(?:eter)?\\s+.*");
copy = stripLinesWithTagFromJavadoc(copy, "@returns?\\s+.*");
return new String[] {copy, javadoc};
}
/** Splits the javadoc into the section to be copied (ret[0]) and the section to replace the original with (ret[1]) */
public String[] split(String javadoc) {
return new String[] {javadoc, javadoc};
}
}
/**
* Copies javadoc on one node to the other.
*
* in 'GETTER' copyMode, first a 'GETTER' segment is searched for. If it exists, that will become the javadoc for the 'to' node, and this section is
* stripped out of the 'from' node. If no 'GETTER' segment is found, then the entire javadoc is taken minus any {@code @param} lines. any {@code @return} lines
* are stripped from 'from'.
*
* in 'SETTER' mode, stripping works similarly to 'GETTER' mode, except {@code param} are copied and stripped from the original and {@code @return} are skipped.
*/
public static void copyJavadoc(JavacNode from, JCTree to, CopyJavadoc copyMode) {
if (copyMode == null) copyMode = CopyJavadoc.VERBATIM;
try {
JCCompilationUnit cu = ((JCCompilationUnit) from.top().get());
if (cu.docComments != null) {
String javadoc = cu.docComments.get(from.get());
if (javadoc != null) {
String[] filtered = copyMode.split(javadoc);
cu.docComments.put(to, filtered[0]);
cu.docComments.put(from.get(), filtered[1]);
}
}
} catch (Exception ignore) {}
}
}
@@ -0,0 +1,148 @@
/*
* Copyright (C) 2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.javac.handlers;
import static lombok.javac.Javac.*;
import static lombok.javac.handlers.JavacHandlerUtil.*;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCBinary;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCIf;
import com.sun.tools.javac.tree.JCTree.JCLiteral;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCParens;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCThrow;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.util.List;
import lombok.NonNull;
import lombok.core.AnnotationValues;
import lombok.core.AST.Kind;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
@ProviderFor(JavacAnnotationHandler.class)
public class NonNullHandler extends JavacAnnotationHandler<NonNull> {
@Override public void handle(AnnotationValues<NonNull> annotation, JCAnnotation ast, JavacNode annotationNode) {
if (annotationNode.up().getKind() == Kind.FIELD) {
// This is meaningless unless the field is used to generate a method (@Setter, @RequiredArgsConstructor, etc),
// but in that case those handlers will take care of it. However, we DO check if the annotation is applied to
// a primitive, because those handlers trigger on any annotation named @NonNull and we only want the warning
// behaviour on _OUR_ 'lombok.NonNull'.
try {
if (isPrimitive(((JCVariableDecl) annotationNode.up().get()).vartype)) {
annotationNode.addWarning("@NonNull is meaningless on a primitive.");
}
} catch (Exception ignore) {}
return;
}
if (annotationNode.up().getKind() != Kind.ARGUMENT) return;
JCMethodDecl declaration;
try {
declaration = (JCMethodDecl) annotationNode.up().up().get();
} catch (Exception e) {
return;
}
if (JavacHandlerUtil.isGenerated(declaration)) return;
// Possibly, if 'declaration instanceof ConstructorDeclaration', fetch declaration.constructorCall, search it for any references to our parameter,
// and if they exist, create a new method in the class: 'private static <T> T lombok$nullCheck(T expr, String msg) {if (expr == null) throw NPE; return expr;}' and
// wrap all references to it in the super/this to a call to this method.
JCStatement nullCheck = recursiveSetGeneratedBy(generateNullCheck(annotationNode.getTreeMaker(), annotationNode.up()), ast);
if (nullCheck == null) {
// @NonNull applied to a primitive. Kinda pointless. Let's generate a warning.
annotationNode.addWarning("@NonNull is meaningless on a primitive.");
return;
}
List<JCStatement> statements = declaration.body.stats;
String expectedName = annotationNode.up().getName();
for (JCStatement stat : statements) {
if (JavacHandlerUtil.isConstructorCall(stat)) continue;
String varNameOfNullCheck = returnVarNameIfNullCheck(stat);
if (varNameOfNullCheck == null) break;
if (varNameOfNullCheck.equals(expectedName)) return;
}
List<JCStatement> tail = statements;
List<JCStatement> head = List.nil();
for (JCStatement stat : statements) {
if (JavacHandlerUtil.isConstructorCall(stat) || JavacHandlerUtil.isGenerated(stat)) {
tail = tail.tail;
head = head.prepend(stat);
continue;
}
break;
}
List<JCStatement> newList = tail.prepend(nullCheck);
for (JCStatement stat : head) newList = newList.prepend(stat);
declaration.body.stats = newList;
}
/**
* Checks if the statement is of the form 'if (x == null) {throw WHATEVER;},
* where the block braces are optional. If it is of this form, returns "x".
* If it is not of this form, returns null.
*/
private String returnVarNameIfNullCheck(JCStatement stat) {
if (!(stat instanceof JCIf)) return null;
/* Check that the if's statement is a throw statement, possibly in a block. */ {
JCStatement then = ((JCIf) stat).thenpart;
if (then instanceof JCBlock) {
List<JCStatement> stats = ((JCBlock) then).stats;
if (stats.length() == 0) return null;
then = stats.get(0);
}
if (!(then instanceof JCThrow)) return null;
}
/* Check that the if's conditional is like 'x == null'. Return from this method (don't generate
a nullcheck) if 'x' is equal to our own variable's name: There's already a nullcheck here. */ {
JCExpression cond = ((JCIf) stat).cond;
while (cond instanceof JCParens) cond = ((JCParens) cond).expr;
if (!(cond instanceof JCBinary)) return null;
JCBinary bin = (JCBinary) cond;
if (compareCTC(getTag(bin), CTC_EQUAL)) return null;
if (!(bin.lhs instanceof JCIdent)) return null;
if (!(bin.rhs instanceof JCLiteral)) return null;
if (compareCTC(getTypeTag((JCLiteral) bin.rhs), CTC_BOT)) return null;
return ((JCIdent) bin.lhs).name.toString();
}
}
}
@@ -21,17 +21,16 @@
*/
package lombok.delombok;
import lombok.javac.Javac;
import lombok.javac.Javac6BasedLombokOptions;
import lombok.javac.Javac8BasedLombokOptions;
import lombok.javac.LombokOptions;
import com.sun.tools.javac.main.JavaCompiler;
import com.sun.tools.javac.util.Context;
public class LombokOptionsFactory {
enum LombokOptionCompilerVersion {
JDK6_7 {
JDK7_AND_LOWER {
@Override LombokOptions createAndRegisterOptions(Context context) {
return Javac6BasedLombokOptions.replaceWithDelombokOptions(context);
}
@@ -46,17 +45,13 @@ public class LombokOptionsFactory {
abstract LombokOptions createAndRegisterOptions(Context context);
}
public static LombokOptions getDelombokOptions(Context context) {
LombokOptions options;
if (JavaCompiler.version().startsWith("1.6") || JavaCompiler.version().startsWith("1.7")) {
options = LombokOptionCompilerVersion.JDK6_7.createAndRegisterOptions(context);
} else if (JavaCompiler.version().startsWith("1.8")) {
options = LombokOptionCompilerVersion.JDK8.createAndRegisterOptions(context);
if (Javac.getJavaCompilerVersion() < 8) {
options = LombokOptionCompilerVersion.JDK7_AND_LOWER.createAndRegisterOptions(context);
} else {
throw new IllegalStateException("No support for compiler version " + JavaCompiler.version() + " for delombok");
options = LombokOptionCompilerVersion.JDK8.createAndRegisterOptions(context);
}
return options;
}
}
@@ -22,6 +22,11 @@
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/*
* Code derived from com.sun.tools.javac.tree.Pretty, from the langtools project.
* A version can be found at, for example, http://hg.openjdk.java.net/jdk7/build/langtools
*/
package lombok.delombok;
import static com.sun.tools.javac.code.Flags.ANNOTATION;
@@ -220,12 +225,21 @@ public class PrettyCommentsPrinter extends JCTree.Visitor {
return tree.getEndPosition(cu.endPositions);
}
private void consumeComments(int till) throws IOException {
private void consumeComments(int until) throws IOException {
consumeComments(until, null);
}
private void consumeComments(int until, JCTree tree) throws IOException {
boolean prevNewLine = onNewLine;
boolean found = false;
CommentInfo head = comments.head;
while (comments.nonEmpty() && head.pos < till) {
printComment(head);
while (comments.nonEmpty() && head.pos < until) {
if (tree != null && docComments != null && docComments.containsKey(tree) && head.isJavadoc() && noFurtherJavadocForthcoming(until)) {
// This is (presumably) the exact same javadoc that has already been associated with the node that we're just about to
// print. These javadoc can be modified by lombok handlers, and as such we should NOT print them from the consumed comments db,
// and instead print the actual javadoc associated with the upcoming node (which the visit method for that node will take care of).
} else {
printComment(head);
}
comments = comments.tail;
head = comments.head;
}
@@ -233,6 +247,17 @@ public class PrettyCommentsPrinter extends JCTree.Visitor {
println();
}
}
private boolean noFurtherJavadocForthcoming(int until) {
List<CommentInfo> c = comments;
if (c.nonEmpty()) c = c.tail;
while (c.nonEmpty()) {
if (c.head.pos >= until) return true;
if (c.head.isJavadoc()) return false;
c = c.tail;
}
return true;
}
private void consumeTrailingComments(int from) throws IOException {
boolean prevNewLine = onNewLine;
@@ -404,7 +429,7 @@ public class PrettyCommentsPrinter extends JCTree.Visitor {
this.prec = prec;
if (tree == null) print("/*missing*/");
else {
consumeComments(tree.pos);
consumeComments(tree.pos, tree);
tree.accept(this);
int endPos = endPos(tree);
consumeTrailingComments(endPos);
@@ -506,7 +531,14 @@ public class PrettyCommentsPrinter extends JCTree.Visitor {
print("/**"); println();
int pos = 0;
int endpos = lineEndPos(dc, pos);
boolean atStart = true;
while (pos < dc.length()) {
String line = dc.substring(pos, endpos);
if (line.trim().isEmpty() && atStart) {
atStart = false;
continue;
}
atStart = false;
align();
print(" *");
if (pos < dc.length() && dc.charAt(pos) > ' ') print(" ");
@@ -616,7 +648,7 @@ public class PrettyCommentsPrinter extends JCTree.Visitor {
docComments = tree.docComments;
printDocComment(tree);
if (tree.pid != null) {
consumeComments(tree.pos);
consumeComments(tree.pos, tree);
print("package ");
printExpr(tree.pid);
print(";");
@@ -690,7 +722,7 @@ public class PrettyCommentsPrinter extends JCTree.Visitor {
public void visitClassDef(JCClassDecl tree) {
try {
consumeComments(tree.pos);
consumeComments(tree.pos, tree);
println(); align();
printDocComment(tree);
printAnnotations(tree.mods.annotations);
@@ -982,6 +1014,29 @@ public class PrettyCommentsPrinter extends JCTree.Visitor {
public void visitTry(JCTry tree) {
try {
print("try ");
List<?> resources = null;
try {
Field f = JCTry.class.getField("resources");
resources = (List<?>) f.get(tree);
} catch (Exception ignore) {
// In JDK6 and down this field does not exist; resources will retain its initializer value which is what we want.
}
if (resources != null && resources.nonEmpty()) {
boolean first = true;
print("(");
for (Object var0 : resources) {
JCTree var = (JCTree) var0;
if (!first) {
println();
indent();
}
printStat(var);
first = false;
}
print(") ");
}
printStat(tree.body);
for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
printStat(l.head);
@@ -267,7 +267,7 @@ public class EclipsePatcher extends Agent {
.build());
}
private static void patchPostCompileHookEclipse(ScriptManager sm) {
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.core.builder.IncrementalImageBuilder", "writeClassFileContents"))
@@ -236,10 +236,8 @@ public class PatchExtensionMethod {
for (int i = 0, iend = arguments.size(); i < iend; i++) {
Expression arg = arguments.get(i);
if (fixedBinding.parameters[i].isArrayType() != arg.resolvedType.isArrayType()) break;
if (arg.resolvedType.isArrayType()) {
if (arg instanceof MessageSend) {
((MessageSend) arg).valueCast = arg.resolvedType;
}
if (arg instanceof MessageSend) {
((MessageSend) arg).valueCast = arg.resolvedType;
}
if (!fixedBinding.parameters[i].isBaseType() && arg.resolvedType.isBaseType()) {
int id = arg.resolvedType.id;
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2012 The Project Lombok Authors.
* Copyright (C) 2010-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -329,7 +329,7 @@ public class PatchFixes {
return newSimpleNames;
}
public static byte[] runPostCompiler(byte[] bytes, String fileName) {
public static byte[] runPostCompiler(byte[] bytes, String fileName) {
byte[] transformed = PostCompiler.applyTransformations(bytes, fileName, DiagnosticsReceiver.CONSOLE);
return transformed == null ? bytes : transformed;
}
@@ -25,6 +25,7 @@ import java.io.File;
import java.io.IOException;
import java.net.URL;
import lombok.installer.eclipse.EclipseFinder;
import lombok.patcher.inject.LiveInjector;
/**
@@ -66,11 +67,12 @@ public abstract class IdeLocation {
}
private static final String LEGAL_PATH_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_/";
private static final String LEGAL_PATH_CHARS_WINDOWS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_/:\\ ";
public static String escapePath(String path) {
StringBuilder out = new StringBuilder();
String legalChars = IdeFinder.getOS() == EclipseFinder.OS.UNIX ? LEGAL_PATH_CHARS : LEGAL_PATH_CHARS_WINDOWS;
for (char c : path.toCharArray()) {
if (LEGAL_PATH_CHARS.indexOf(c) == -1) out.append('\\');
if (legalChars.indexOf(c) == -1) out.append('\\');
out.append(c);
}
return out.toString();
@@ -64,14 +64,10 @@ public class EclipseFinder extends IdeFinder {
/**
* Returns a list of paths of Eclipse installations.
* Eclipse installations are found by checking for the existence of 'eclipse.exe' in the following locations:
* <ul>
* <li>X:\*Program Files*\*Eclipse*</li>
* <li>X:\*Eclipse*</li>
* </ul>
*
* Where 'X' is tried for all local disk drives, unless there's a problem calling fsutil, in which case only
* C: is tried.
* The search process works by scanning for each 'source dir' for either an eclipse installation or a folder containing the text returned
* by getDirName(). If such a folder is found, this process is applied recursively. On windows, this process is run on each drive letter
* which represents a physical hard disk. If the native windows API call to determine these drive letters fails, only 'C:' is checked.
*/
private List<String> getSourceDirsOnWindowsWithDriveLetters() {
List<String> driveLetters = asList("C");
@@ -83,12 +79,22 @@ public class EclipseFinder extends IdeFinder {
List<String> sourceDirs = new ArrayList<String>();
for (String letter : driveLetters) {
for (String possibleSource : getSourceDirsOnWindows()) {
sourceDirs.add(letter + ":" + possibleSource);
if (!isDriveSpecificOnWindows(possibleSource)) {
sourceDirs.add(letter + ":" + possibleSource);
}
}
}
for (String possibleSource : getSourceDirsOnWindows()) {
if (isDriveSpecificOnWindows(possibleSource)) sourceDirs.add(possibleSource);
}
return sourceDirs;
}
public boolean isDriveSpecificOnWindows(String path) {
return path.length() > 1 && path.charAt(1) == ':';
}
protected List<String> getSourceDirsOnMac() {
return Arrays.asList("/Applications", System.getProperty("user.home", "."));
}
@@ -0,0 +1,70 @@
/*
* Copyright (C) 2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.installer.eclipse;
import java.util.Arrays;
import java.util.List;
import lombok.installer.CorruptedIdeLocationException;
import lombok.installer.IdeFinder;
import lombok.installer.IdeLocation;
import org.mangosdk.spi.ProviderFor;
/**
* JBDS (JBoss Developer Studio) is an eclipse variant.
* Other than different executable names, it's the same as eclipse, as far as lombok support goes.
*/
@ProviderFor(IdeFinder.class)
public class JbdsFinder extends EclipseFinder {
@Override protected IdeLocation createLocation(String guess) throws CorruptedIdeLocationException {
return new JbdsLocationProvider().create0(guess);
}
@Override protected String getDirName() {
return "studio";
}
@Override protected String getMacExecutableName() {
return "jbdevstudio.app";
}
@Override protected String getUnixExecutableName() {
return "jbdevstudio";
}
@Override protected String getWindowsExecutableName() {
return "jbdevstudio.exe";
}
@Override protected List<String> getSourceDirsOnWindows() {
return Arrays.asList("\\", "\\Program Files", "\\Program Files (x86)", System.getProperty("user.home", "."));
}
@Override protected List<String> getSourceDirsOnMac() {
return Arrays.asList("/Applications", System.getProperty("user.home", "."));
}
@Override protected List<String> getSourceDirsOnUnix() {
return Arrays.asList(System.getProperty("user.home", "."));
}
}
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.installer.eclipse;
import java.io.File;
import java.net.URL;
import lombok.installer.CorruptedIdeLocationException;
public class JbdsLocation extends EclipseLocation {
public JbdsLocation(String nameOfLocation, File pathToEclipseIni) throws CorruptedIdeLocationException {
super(nameOfLocation, pathToEclipseIni);
}
@Override public URL getIdeIcon() {
return JbdsLocation.class.getResource("jbds.png");
}
@Override protected String getIniFileName() {
return "jbdevstudio.ini";
}
@Override protected String getTypeName() {
return "JBoss Developer Studio";
}
}
@@ -0,0 +1,69 @@
/*
* Copyright (C) 2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.installer.eclipse;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import lombok.installer.CorruptedIdeLocationException;
import lombok.installer.IdeLocation;
import lombok.installer.IdeLocationProvider;
import lombok.installer.IdeFinder.OS;
import org.mangosdk.spi.ProviderFor;
@ProviderFor(IdeLocationProvider.class)
public class JbdsLocationProvider extends EclipseLocationProvider {
@Override protected List<String> getEclipseExecutableNames() {
return Arrays.asList("jbdevstudio.app", "jbdevstudio.exe", "jbdevstudioc.exe", "jbdevstudio");
}
@Override protected String getIniName() {
return "jbdevstudio.ini";
}
@Override protected IdeLocation makeLocation(String name, File ini) throws CorruptedIdeLocationException {
return new JbdsLocation(name, ini);
}
@Override protected String getMacAppName() {
return "jbdevstudio.app";
}
@Override protected String getUnixAppName() {
return "jbdevstudio";
}
@Override public Pattern getLocationSelectors(OS os) {
switch (os) {
case MAC_OS_X:
return Pattern.compile("^(jbdevstudio|jbdevstudio\\.ini|jbdevstudio\\.app)$", Pattern.CASE_INSENSITIVE);
case WINDOWS:
return Pattern.compile("^(jbdevstudioc?\\.exe|jbdevstudio\\.ini)$", Pattern.CASE_INSENSITIVE);
default:
case UNIX:
return Pattern.compile("^(jbdevstudio|jbdevstudio\\.ini)$", Pattern.CASE_INSENSITIVE);
}
}
}
@@ -57,7 +57,7 @@ public class STSFinder extends EclipseFinder {
}
@Override protected List<String> getSourceDirsOnWindows() {
return Arrays.asList("\\", "\\springsource", "\\Program Files", "\\Program Files\\springsource", System.getProperty("user.home", "."), System.getProperty("user.home", ".") + "\\springsource");
return Arrays.asList("\\", "\\springsource", "\\Program Files", "\\Program Files (x86)", "\\Program Files\\springsource", "\\Program Files (x86)\\springsource", System.getProperty("user.home", "."), System.getProperty("user.home", ".") + "\\springsource");
}
@Override protected List<String> getSourceDirsOnMac() {
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

@@ -0,0 +1,25 @@
/*
* These are stub versions of various bits of javac-internal API (for various different versions of javac). Lombok is compiled against these.
*/
package com.sun.tools.javac.parser;
import java.nio.CharBuffer;
public class DocCommentScanner extends Scanner {
protected DocCommentScanner(Factory fac, CharBuffer buffer) {
super(fac, buffer);
}
protected DocCommentScanner(Factory fac, char[] input, int inputLength) {
super(fac, input, inputLength);
}
protected DocCommentScanner(ScannerFactory fac, CharBuffer buffer) {
super(fac, buffer);
}
protected DocCommentScanner(ScannerFactory fac, char[] input, int inputLength) {
super(fac, input, inputLength);
}
}
@@ -59,4 +59,11 @@ public class Scanner implements Lexer {
public char[] getRawCharacters(int beginIndex, int endIndex) {
return null;
}
public void nextToken() {
}
public char[] getRawCharacters() {
return new char[0];
}
}
@@ -0,0 +1,25 @@
/*
* These are stub versions of various bits of javac-internal API (for various different versions of javac). Lombok is compiled against these.
*/
package com.sun.tools.javadoc;
import java.nio.CharBuffer;
import com.sun.tools.javac.parser.Scanner;
import com.sun.tools.javac.util.Context;
public class DocCommentScanner extends Scanner {
protected DocCommentScanner(com.sun.tools.javadoc.DocCommentScanner.Factory fac, CharBuffer buffer) {
super(fac, buffer);
}
protected DocCommentScanner(com.sun.tools.javadoc.DocCommentScanner.Factory fac, char[] input, int inputLength) {
super(fac, input, inputLength);
}
public static class Factory extends Scanner.Factory {
protected Factory(Context context) {
super(context);
}
}
}
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.core;
/**
* Utility functions for validating potential java verifiers.
*/
public class JavaIdentifiers {
private JavaIdentifiers() {}
private static final LombokImmutableList<String> KEYWORDS = LombokImmutableList.of(
"public", "private", "protected",
"default", "switch", "case",
"for", "do", "goto", "const", "strictfp", "while", "if", "else",
"byte", "short", "int", "long", "float", "double", "void", "boolean", "char",
"null", "false", "true",
"continue", "break", "return", "instanceof",
"synchronized", "volatile", "transient", "final", "static",
"interface", "class", "extends", "implements", "throws",
"throw", "catch", "try", "finally", "abstract", "assert",
"enum", "import", "package", "native", "new", "super", "this");
public static boolean isValidJavaIdentifier(String identifier) {
if (identifier == null) return false;
if (identifier.isEmpty()) return false;
if (!Character.isJavaIdentifierStart(identifier.charAt(0))) return false;
for (int i = 1; i < identifier.length(); i++) {
if (!Character.isJavaIdentifierPart(identifier.charAt(i))) return false;
}
return !isKeyword(identifier);
}
public static boolean isKeyword(String keyword) {
return KEYWORDS.contains(keyword);
}
}
@@ -0,0 +1,188 @@
/*
* Copyright (C) 2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.core;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
public final class LombokImmutableList<T> implements Iterable<T> {
private Object[] content;
private static final LombokImmutableList<?> EMPTY = new LombokImmutableList<Object>(new Object[0]);
@SuppressWarnings("unchecked")
public static <T> LombokImmutableList<T> of() {
return (LombokImmutableList<T>) EMPTY;
}
public static <T> LombokImmutableList<T> of(T a) {
return new LombokImmutableList<T>(new Object[] {a});
}
public static <T> LombokImmutableList<T> of(T a, T b) {
return new LombokImmutableList<T>(new Object[] {a, b});
}
public static <T> LombokImmutableList<T> of(T a, T b, T c) {
return new LombokImmutableList<T>(new Object[] {a, b, c});
}
public static <T> LombokImmutableList<T> of(T a, T b, T c, T d) {
return new LombokImmutableList<T>(new Object[] {a, b, c, d});
}
public static <T> LombokImmutableList<T> of(T a, T b, T c, T d, T e) {
return new LombokImmutableList<T>(new Object[] {a, b, c, d, e});
}
public static <T> LombokImmutableList<T> of(T a, T b, T c, T d, T e, T f, T... g) {
Object[] rest = g == null ? new Object[] {null} : g;
Object[] val = new Object[rest.length + 6];
System.arraycopy(rest, 0, val, 6, rest.length);
val[0] = a;
val[1] = b;
val[2] = c;
val[3] = d;
val[4] = e;
val[5] = f;
return new LombokImmutableList<T>(val);
}
public static <T> LombokImmutableList<T> copyOf(Collection<? extends T> list) {
return new LombokImmutableList<T>(list.toArray());
}
public static <T> LombokImmutableList<T> copyOf(Iterable<? extends T> iterable) {
List<T> list = new ArrayList<T>();
for (T o : iterable) list.add(o);
return copyOf(list);
}
private LombokImmutableList(Object[] content) {
this.content = content;
}
public LombokImmutableList<T> replaceElementAt(int idx, T newValue) {
Object[] newContent = content.clone();
newContent[idx] = newValue;
return new LombokImmutableList<T>(newContent);
}
public LombokImmutableList<T> append(T newValue) {
int len = content.length;
Object[] newContent = new Object[len + 1];
System.arraycopy(content, 0, newContent, 0, len);
newContent[len] = newValue;
return new LombokImmutableList<T>(newContent);
}
public LombokImmutableList<T> prepend(T newValue) {
int len = content.length;
Object[] newContent = new Object[len + 1];
System.arraycopy(content, 0, newContent, 1, len);
newContent[0] = newValue;
return new LombokImmutableList<T>(newContent);
}
public int indexOf(T val) {
int len = content.length;
if (val == null) {
for (int i = 0; i < len; i++) if (content[i] == null) return i;
return -1;
}
for (int i = 0; i < len; i++) if (val.equals(content[i])) return i;
return -1;
}
public LombokImmutableList<T> removeElement(T val) {
int idx = indexOf(val);
return idx == -1 ? this : removeElementAt(idx);
}
public LombokImmutableList<T> removeElementAt(int idx) {
int len = content.length;
Object[] newContent = new Object[len - 1];
if (idx > 0) System.arraycopy(content, 0, newContent, 0, idx);
if (idx < len - 1) System.arraycopy(content, idx + 1, newContent, idx, len - idx - 1);
return new LombokImmutableList<T>(newContent);
}
public boolean isEmpty() {
return content.length == 0;
}
public int size() {
return content.length;
}
@SuppressWarnings("unchecked")
public T get(int idx) {
return (T) content[idx];
}
public boolean contains(T in) {
if (in == null) {
for (Object e : content) if (e == null) return true;
return false;
}
for (Object e : content) if (in.equals(e)) return true;
return false;
}
public Iterator<T> iterator() {
return new Iterator<T>() {
private int idx = 0;
@Override public boolean hasNext() {
return idx < content.length;
}
@SuppressWarnings("unchecked")
@Override public T next() {
if (idx < content.length) return (T) content[idx++];
throw new NoSuchElementException();
}
@Override public void remove() {
throw new UnsupportedOperationException("List is immutable");
}
};
}
@Override public String toString() {
return Arrays.toString(content);
}
@Override public boolean equals(Object obj) {
if (!(obj instanceof LombokImmutableList)) return false;
if (obj == this) return true;
return Arrays.equals(content, ((LombokImmutableList<?>) obj).content);
}
@Override public int hashCode() {
return Arrays.hashCode(content);
}
}
+62 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2011 The Project Lombok Authors.
* Copyright (C) 2009-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,6 +21,7 @@
*/
package lombok.eclipse;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -36,8 +37,11 @@ import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Literal;
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.TryStatement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
public class Eclipse {
@@ -58,7 +62,9 @@ public class Eclipse {
* but we need to deal with it. This turns [[java][lang][String]] into "java.lang.String".
*/
public static String toQualifiedName(char[][] typeName) {
StringBuilder sb = new StringBuilder();
int len = typeName.length - 1;
for (char[] c : typeName) len += c.length;
StringBuilder sb = new StringBuilder(len);
boolean first = true;
for (char[] c : typeName) {
sb.append(first ? "" : ".").append(c);
@@ -175,4 +181,58 @@ public class Eclipse {
return null;
}
private static long latestEcjCompilerVersionConstantCached = 0;
public static long getLatestEcjCompilerVersionConstant() {
if (latestEcjCompilerVersionConstantCached != 0) return latestEcjCompilerVersionConstantCached;
int highestVersionSoFar = 0;
for (Field f : ClassFileConstants.class.getDeclaredFields()) {
try {
if (f.getName().startsWith("JDK1_")) {
int thisVersion = Integer.parseInt(f.getName().substring("JDK1_".length()));
if (thisVersion > highestVersionSoFar) {
highestVersionSoFar = thisVersion;
latestEcjCompilerVersionConstantCached = (Long) f.get(null);
}
}
} catch (Exception ignore) {}
}
if (highestVersionSoFar > 6 && !ecjSupportsJava7Features()) {
latestEcjCompilerVersionConstantCached = ClassFileConstants.JDK1_6;
}
return latestEcjCompilerVersionConstantCached;
}
private static int ecjCompilerVersionCached = -1;
public static int getEcjCompilerVersion() {
if (ecjCompilerVersionCached >= 0) return ecjCompilerVersionCached;
for (Field f : CompilerOptions.class.getDeclaredFields()) {
try {
if (f.getName().startsWith("VERSION_1_")) {
ecjCompilerVersionCached = Math.max(ecjCompilerVersionCached, Integer.parseInt(f.getName().substring("VERSION_1_".length())));
}
} catch (Exception ignore) {}
}
if (ecjCompilerVersionCached < 5) ecjCompilerVersionCached = 5;
if (!ecjSupportsJava7Features()) ecjCompilerVersionCached = Math.min(6, ecjCompilerVersionCached);
return ecjCompilerVersionCached;
}
/**
* Certain ECJ versions that only go up to -source 6 report that they support -source 7 and even fail to error when -source 7 is applied.
* We detect this and correctly say that no more than -source 6 is supported. (when this is the case, this method returns false).
*/
private static boolean ecjSupportsJava7Features() {
try {
TryStatement.class.getDeclaredField("resources");
return true;
} catch (NoSuchFieldException e) {
return false;
}
}
}
+20 -20
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011 The Project Lombok Authors.
* Copyright (C) 2011-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,9 +21,12 @@
*/
package lombok.javac;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.WeakHashMap;
import lombok.Lombok;
import com.sun.tools.javac.main.JavaCompiler;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.util.Context;
@@ -62,36 +65,33 @@ public class CommentCatcher {
private static void registerCommentsCollectingScannerFactory(Context context) {
try {
if (JavaCompiler.version().startsWith("1.6")) {
Class.forName("lombok.javac.java6.CommentCollectingScannerFactory").getMethod("preRegister", Context.class).invoke(null, context);
} else if (JavaCompiler.version().startsWith("1.7") || JavaCompiler.version().startsWith("1.8")) {
Class.forName("lombok.javac.java7.CommentCollectingScannerFactory").getMethod("preRegister", Context.class).invoke(null, context);
Class<?> scannerFactory;
if (Javac.getJavaCompilerVersion() <= 6) {
scannerFactory = Class.forName("lombok.javac.java6.CommentCollectingScannerFactory");
} else {
throw new IllegalStateException("No comments parser for compiler version " + JavaCompiler.version());
scannerFactory = Class.forName("lombok.javac.java7.CommentCollectingScannerFactory");
}
scannerFactory.getMethod("preRegister", Context.class).invoke(null, context);
} catch (InvocationTargetException e) {
throw Lombok.sneakyThrow(e.getCause());
} catch (Exception e) {
if (e instanceof RuntimeException) throw (RuntimeException)e;
throw new RuntimeException(e);
throw Lombok.sneakyThrow(e);
}
}
private static void setInCompiler(JavaCompiler compiler, Context context, Map<JCCompilationUnit, List<CommentInfo>> commentsMap) {
try {
if (JavaCompiler.version().startsWith("1.6")) {
Class<?> parserFactory = Class.forName("lombok.javac.java6.CommentCollectingParserFactory");
parserFactory.getMethod("setInCompiler",JavaCompiler.class, Context.class, Map.class).invoke(null, compiler, context, commentsMap);
} else if (JavaCompiler.version().startsWith("1.7") || JavaCompiler.version().startsWith("1.8")) {
Class<?> parserFactory = Class.forName("lombok.javac.java7.CommentCollectingParserFactory");
parserFactory.getMethod("setInCompiler",JavaCompiler.class, Context.class, Map.class).invoke(null, compiler, context, commentsMap);
Class<?> parserFactory;
if (Javac.getJavaCompilerVersion() <= 6) {
parserFactory = Class.forName("lombok.javac.java6.CommentCollectingParserFactory");
} else {
throw new IllegalStateException("No comments parser for compiler version " + JavaCompiler.version());
parserFactory = Class.forName("lombok.javac.java7.CommentCollectingParserFactory");
}
parserFactory.getMethod("setInCompiler", JavaCompiler.class, Context.class, Map.class).invoke(null, compiler, context, commentsMap);
} catch (InvocationTargetException e) {
throw Lombok.sneakyThrow(e.getCause());
} catch (Exception e) {
if (e instanceof RuntimeException) throw (RuntimeException)e;
throw new RuntimeException(e);
throw Lombok.sneakyThrow(e);
}
}
}
+1 -1
View File
@@ -66,7 +66,7 @@ public final class CommentInfo {
}
public boolean isJavadoc() {
return content.startsWith("/**");
return content.startsWith("/**") && content.length() > 4;
}
@Override
@@ -1,11 +0,0 @@
package lombok.javac;
import com.sun.tools.javac.code.Symbol.TypeSymbol;
import com.sun.tools.javac.code.Type;
public class JCNoTypeFactory {
public static final Type getJCNotType(Object typeTag, TypeSymbol tsym) {
}
}
+286 -89
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2012 The Project Lombok Authors.
* Copyright (C) 2009-2013 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,19 +21,38 @@
*/
package lombok.javac;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;
import java.lang.reflect.Modifier;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.lang.model.type.NoType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeVisitor;
import lombok.Lombok;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.main.JavaCompiler;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCBinary;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCLiteral;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCUnary;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name;
/**
* Container for static utility methods relevant to lombok's operation on javac.
@@ -46,6 +65,30 @@ public class Javac {
/** Matches any of the 8 primitive names, such as {@code boolean}. */
private static final Pattern PRIMITIVE_TYPE_NAME_PATTERN = Pattern.compile("^(boolean|byte|short|int|long|float|double|char)$");
private static final Pattern VERSION_PARSER = Pattern.compile("^(\\d{1,6})\\.(\\d{1,6}).*$");
private static final AtomicInteger compilerVersion = new AtomicInteger(-1);
/**
* Returns the version of this java compiler, i.e. the JDK that it shipped in. For example, for javac v1.7, this returns {@code 7}.
*/
public static int getJavaCompilerVersion() {
int cv = compilerVersion.get();
if (cv != -1) return cv;
Matcher m = VERSION_PARSER.matcher(JavaCompiler.version());
if (m.matches()) {
int major = Integer.parseInt(m.group(1));
int minor = Integer.parseInt(m.group(2));
if (major == 1) {
compilerVersion.set(minor);
return minor;
}
}
compilerVersion.set(6);
return 6;
}
/**
* Checks if the given expression (that really ought to refer to a type
* expression) represents a primitive type.
@@ -102,56 +145,53 @@ public class Javac {
public static final Object CTC_EQUAL = getTreeTag("EQ");
public static boolean compareCTC(Object ctc1, Object ctc2) {
return Objects.equals(ctc1, ctc2);
return ctc1 == null ? ctc2 == null : ctc1.equals(ctc2);
}
private static final ConcurrentMap<String, Object> TYPE_TAG_CACHE = new ConcurrentHashMap<String, Object>();
private static final ConcurrentMap<String, Object> TREE_TAG_CACHE = new ConcurrentHashMap<String, Object>();
/**
* Retrieves the current type tag. The actual type object differs depending on the Compiler version
*
* For JDK 8 this is an enum value of type <code>com.sun.tools.javac.code.TypeTag</code>
* for JDK 7 and lower, this is the value of the constant within <code>com.sun.tools.javac.code.TypeTags</code>
* Retrieves the provided TypeTag value, in a compiler version independent manner.
*
* The actual type object differs depending on the Compiler version:
* <ul>
* <li>For JDK 8 this is an enum value of type <code>com.sun.tools.javac.code.TypeTag</code>
* <li>for JDK 7 and lower, this is the value of the constant within <code>com.sun.tools.javac.code.TypeTags</code>
* </ul>
* Solves the problem of compile time constant inlining, resulting in lombok
* having the wrong value (javac compiler changes private api constants from
* time to time).
*
* @param identifier
* @return the ordinal value of the typetag constant
* @param identifier Identifier to turn into a TypeTag.
* @return the value of the typetag constant (either enum instance or an Integer object).
*/
public static Object getTypeTag(String identifier) {
try {
if (JavaCompiler.version().startsWith("1.8")) {
return Class.forName("com.sun.tools.javac.code.TypeTag").getField(identifier).get(null);
} else {
return Class.forName("com.sun.tools.javac.code.TypeTags").getField(identifier).get(null);
}
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (Exception e) {
if (e instanceof RuntimeException) throw (RuntimeException) e;
throw new RuntimeException(e);
}
return getFieldCached(TYPE_TAG_CACHE, getJavaCompilerVersion() < 8 ? "com.sun.tools.javac.code.TypeTag" : "com.sun.tools.javac.code.TypeTags", identifier);
}
public static Object getTreeTag(String identifier) {
try {
if (JavaCompiler.version().startsWith("1.8")) {
return Class.forName("com.sun.tools.javac.tree.JCTree$Tag").getField(identifier).get(null);
} else {
return Class.forName("com.sun.tools.javac.tree.JCTree").getField(identifier).get(null);
}
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (Exception e) {
if (e instanceof RuntimeException) throw (RuntimeException) e;
throw new RuntimeException(e);
}
return getFieldCached(TREE_TAG_CACHE, getJavaCompilerVersion() < 8 ? "com.sun.tools.javac.tree.JCTree" : "com.sun.tools.javac.tree.JCTree$Tag", identifier);
}
private static Object getFieldCached(ConcurrentMap<String, Object> cache, String className, String fieldName) {
Object value = cache.get(fieldName);
if (value != null) return value;
try {
value = Class.forName(className).getField(fieldName).get(null);
} catch (NoSuchFieldException e) {
throw Lombok.sneakyThrow(e);
} catch (IllegalAccessException e) {
throw Lombok.sneakyThrow(e);
} catch (ClassNotFoundException e) {
throw Lombok.sneakyThrow(e);
}
cache.putIfAbsent(fieldName, value);
return value;
}
public static Object getTreeTypeTag(JCPrimitiveTypeTree tree) {
return tree.typetag;
}
@@ -159,82 +199,239 @@ public class Javac {
public static Object getTreeTypeTag(JCLiteral tree) {
return tree.typetag;
}
private static final Method createIdent, createLiteral, createUnary, createBinary;
static {
if (getJavaCompilerVersion() < 8) {
createIdent = getMethod(TreeMaker.class, "TypeIdent", int.class);
} else {
createIdent = getMethod(TreeMaker.class, "TypeIdent", "com.sun.tools.javac.code.TypeTag");
}
createIdent.setAccessible(true);
if (getJavaCompilerVersion() < 8) {
createLiteral = getMethod(TreeMaker.class, "Literal", int.class, Object.class);
} else {
createLiteral = getMethod(TreeMaker.class, "Literal", "com.sun.tools.javac.code.TypeTag", "java.lang.Object");
}
createLiteral.setAccessible(true);
if (getJavaCompilerVersion() < 8) {
createUnary = getMethod(TreeMaker.class, "Unary", int.class, JCExpression.class);
} else {
createUnary = getMethod(TreeMaker.class, "Unary", "com.sun.tools.javac.code.TypeTag", JCExpression.class.getName());
}
createUnary.setAccessible(true);
if (getJavaCompilerVersion() < 8) {
createBinary = getMethod(TreeMaker.class, "Binary", Integer.TYPE, JCExpression.class, JCExpression.class);
} else {
createBinary = getMethod(TreeMaker.class, "Binary", "com.sun.tools.javac.code.TypeTag", JCExpression.class.getName(), JCExpression.class.getName());
}
createBinary.setAccessible(true);
}
private static Method getMethod(Class<?> clazz, String name, Class<?>... paramTypes) {
try {
return clazz.getMethod(name, paramTypes);
} catch (NoSuchMethodException e) {
throw Lombok.sneakyThrow(e);
}
}
private static Method getMethod(Class<?> clazz, String name, String... paramTypes) {
try {
Class<?>[] c = new Class[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) c[i] = Class.forName(paramTypes[i]);
return clazz.getMethod(name, c);
} catch (NoSuchMethodException e) {
throw Lombok.sneakyThrow(e);
} catch (ClassNotFoundException e) {
throw Lombok.sneakyThrow(e);
}
}
public static JCExpression makeTypeIdent(TreeMaker maker, Object ctc) {
try {
Method createIdent;
if (JavaCompiler.version().startsWith("1.8")) {
createIdent = TreeMaker.class.getMethod("TypeIdent", Class.forName("com.sun.tools.javac.code.TypeTag"));
} else {
createIdent = TreeMaker.class.getMethod("TypeIdent", Integer.TYPE);
}
return (JCExpression) createIdent.invoke(maker, ctc);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (Exception e) {
if (e instanceof RuntimeException) throw (RuntimeException) e;
throw new RuntimeException(e);
throw Lombok.sneakyThrow(e);
} catch (InvocationTargetException e) {
throw Lombok.sneakyThrow(e.getCause());
}
}
public static JCLiteral makeLiteral(TreeMaker maker, Object ctc, Object argument) {
try {
Method createLiteral;
if (JavaCompiler.version().startsWith("1.8")) {
createLiteral = TreeMaker.class.getMethod("Literal", Class.forName("com.sun.tools.javac.code.TypeTag"), Object.class);
} else {
createLiteral = TreeMaker.class.getMethod("Literal", Integer.TYPE, Object.class);
}
return (JCLiteral) createLiteral.invoke(maker, ctc, argument);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (Exception e) {
if (e instanceof RuntimeException) throw (RuntimeException) e;
throw new RuntimeException(e);
throw Lombok.sneakyThrow(e);
} catch (InvocationTargetException e) {
throw Lombok.sneakyThrow(e.getCause());
}
}
public static JCUnary makeUnary(TreeMaker maker, Object ctc, JCExpression argument) {
try {
Method createUnary;
if (JavaCompiler.version().startsWith("1.8")) {
createUnary = TreeMaker.class.getMethod("Unary", Class.forName("com.sun.tools.javac.code.TypeTag"), JCExpression.class);
} else {
createUnary = TreeMaker.class.getMethod("Unary", Integer.TYPE, JCExpression.class);
}
return (JCUnary) createUnary.invoke(maker, ctc, argument);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (Exception e) {
if (e instanceof RuntimeException) throw (RuntimeException) e;
throw new RuntimeException(e);
throw Lombok.sneakyThrow(e);
} catch (InvocationTargetException e) {
throw Lombok.sneakyThrow(e.getCause());
}
}
public static JCBinary makeBinary(TreeMaker maker, Object ctc, JCExpression rhsArgument, JCExpression lhsArgument) {
public static JCBinary makeBinary(TreeMaker maker, Object ctc, JCExpression lhsArgument, JCExpression rhsArgument) {
try {
Method createUnary;
if (JavaCompiler.version().startsWith("1.8")) {
createUnary = TreeMaker.class.getMethod("Binary", Class.forName("com.sun.tools.javac.code.TypeTag"), JCExpression.class, JCExpression.class);
} else {
createUnary = TreeMaker.class.getMethod("Binary", Integer.TYPE, JCExpression.class, JCExpression.class);
}
return (JCBinary) createUnary.invoke(maker, ctc, rhsArgument, lhsArgument);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
return (JCBinary) createBinary.invoke(maker, ctc, lhsArgument, rhsArgument);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (Exception e) {
if (e instanceof RuntimeException) throw (RuntimeException) e;
throw new RuntimeException(e);
throw Lombok.sneakyThrow(e);
} catch (InvocationTargetException e) {
throw Lombok.sneakyThrow(e.getCause());
}
}
private static final Class<?> JC_VOID_TYPE, JC_NO_TYPE;
static {
Class<?> c = null;
try {
c = Class.forName("com.sun.tools.javac.code.Type$JCVoidType");
} catch (Exception ignore) {}
JC_VOID_TYPE = c;
c = null;
try {
c = Class.forName("com.sun.tools.javac.code.Type$JCNoType");
} catch (Exception ignore) {}
JC_NO_TYPE = c;
}
public static Type createVoidType(TreeMaker maker, Object tag) {
if (Javac.getJavaCompilerVersion() < 8) {
return new JCNoType(((Integer) tag).intValue());
} else {
try {
if (compareCTC(tag, CTC_VOID)) {
return (Type) JC_VOID_TYPE.newInstance();
} else {
return (Type) JC_NO_TYPE.newInstance();
}
} catch (IllegalAccessException e) {
throw Lombok.sneakyThrow(e);
} catch (InstantiationException e) {
throw Lombok.sneakyThrow(e);
}
}
}
private static class JCNoType extends Type implements NoType {
public JCNoType(int tag) {
super(tag, null);
}
@Override
public TypeKind getKind() {
if (Javac.compareCTC(tag, CTC_VOID)) return TypeKind.VOID;
if (Javac.compareCTC(tag, CTC_NONE)) return TypeKind.NONE;
throw new AssertionError("Unexpected tag: " + tag);
}
@Override
public <R, P> R accept(TypeVisitor<R, P> v, P p) {
return v.visitNoType(this, p);
}
}
private static final Field JCTREE_TAG, JCLITERAL_TYPETAG, JCPRIMITIVETYPETREE_TYPETAG;
private static final Method JCTREE_GETTAG;
static {
Field f = null;
try {
f = JCTree.class.getDeclaredField("tag");
} catch (NoSuchFieldException e) {}
JCTREE_TAG = f;
f = null;
try {
f = JCLiteral.class.getDeclaredField("typetag");
} catch (NoSuchFieldException e) {}
JCLITERAL_TYPETAG = f;
f = null;
try {
f = JCPrimitiveTypeTree.class.getDeclaredField("typetag");
} catch (NoSuchFieldException e) {}
JCPRIMITIVETYPETREE_TYPETAG = f;
Method m = null;
try {
m = JCTree.class.getDeclaredMethod("getTag");
} catch (NoSuchMethodException e) {}
JCTREE_GETTAG = m;
}
public static Object getTag(JCTree node) {
if (JCTREE_GETTAG != null) {
try {
return JCTREE_GETTAG.invoke(node);
} catch (Exception e) {}
}
try {
return JCTREE_TAG.get(node);
} catch (Exception e) {
throw new IllegalStateException("Can't get node tag");
}
}
public static Object getTypeTag(JCLiteral node) {
try {
return JCLITERAL_TYPETAG.get(node);
} catch (Exception e) {
throw new IllegalStateException("Can't get JCLiteral typetag");
}
}
public static Object getTypeTag(JCPrimitiveTypeTree node) {
try {
return JCPRIMITIVETYPETREE_TYPETAG.get(node);
} catch (Exception e) {
throw new IllegalStateException("Can't get JCPrimitiveTypeTree typetag");
}
}
private static Method classDef;
public static JCClassDecl ClassDef(TreeMaker maker, JCModifiers mods, Name name, List<JCTypeParameter> typarams, JCExpression extending, List<JCExpression> implementing, List<JCTree> defs) {
if (classDef == null) try {
classDef = TreeMaker.class.getDeclaredMethod("ClassDef", JCModifiers.class, Name.class, List.class, JCExpression.class, List.class, List.class);
} catch (NoSuchMethodException ignore) {}
if (classDef == null) try {
classDef = TreeMaker.class.getDeclaredMethod("ClassDef", JCModifiers.class, Name.class, List.class, JCTree.class, List.class, List.class);
} catch (NoSuchMethodException ignore) {}
if (classDef == null) throw new IllegalStateException("Lombok bug #20130617-1310: ClassDef doesn't look like anything we thought it would look like.");
if (!Modifier.isPublic(classDef.getModifiers()) && !classDef.isAccessible()) {
classDef.setAccessible(true);
}
try {
return (JCClassDecl) classDef.invoke(maker, mods, name, typarams, extending, implementing, defs);
} catch (InvocationTargetException e) {
throw sneakyThrow(e.getCause());
} catch (IllegalAccessException e) {
throw sneakyThrow(e.getCause());
}
}
private static RuntimeException sneakyThrow(Throwable t) {
if (t == null) throw new NullPointerException("t");
Javac.<RuntimeException>sneakyThrow0(t);
return null;
}
@SuppressWarnings("unchecked")
private static <T extends Throwable> void sneakyThrow0(Throwable t) throws T {
throw (T)t;
}
}
@@ -25,7 +25,7 @@ public class CommentCollectingParserFactory extends Parser.Factory {
}
@Override public Parser newParser(Lexer S, boolean keepDocComments, boolean genEndPos) {
Object x = new CommentCollectingParser(this, S, keepDocComments, commentsMap);
Object x = new CommentCollectingParser(this, S, true, commentsMap);
return (Parser) x;
// CCP is based on a stub which extends nothing, but at runtime the stub is replaced with either
//javac6's EndPosParser which extends Parser, or javac7's EndPosParser which implements Parser.
Loaded 100 of 219 files, more files were not shown because too many files have changed in this diff. Show more