diff options
74 files changed, 987 insertions, 453 deletions
@@ -24,6 +24,7 @@ Rabea Gransberger <rgra@users.noreply.github.com> Reinier Zwitserloot <reinier@zwitserloot.com> Robbert Jan Grootjans <grootjans@gmail.com> Roel Spilker <r.spilker@gmail.com> +Roland Praml <pram@gmx.de> Sander Koning <askoning@gmail.com> Szymon Pacanowski <spacanowski@gmail.com> Taiki Sugawara <buzz.taiki@gmail.com> @@ -112,7 +112,7 @@ the common tasks and can be called on to run the main aspects of all the sub-scr </target> <target name="ensureBuildDeps" depends="config-ivy,ensureOpenJdk6Rt"> - <ivy:resolve file="buildScripts/ivy.xml" refresh="true" conf="build, javac7" /> + <ivy:resolve file="buildScripts/ivy.xml" refresh="true" conf="build, javac7, moduleBuild" /> <ivy:retrieve /> </target> @@ -163,15 +163,7 @@ the common tasks and can be called on to run the main aspects of all the sub-scr <target name="-ensureJdk9"> <condition property="java.version.insufficient"> - <or> - <equals arg1="${ant.java.version}" arg2="1.2" /> - <equals arg1="${ant.java.version}" arg2="1.3" /> - <equals arg1="${ant.java.version}" arg2="1.4" /> - <equals arg1="${ant.java.version}" arg2="1.5" /> - <equals arg1="${ant.java.version}" arg2="1.6" /> - <equals arg1="${ant.java.version}" arg2="1.7" /> - <equals arg1="${ant.java.version}" arg2="1.8" /> - </or> + <matches string="${java.version}" pattern="^1\.[2-8](\..*)?" /> </condition> <fail if="java.version.insufficient">To compile lombok, you need JDK9 or higher; lombok requires this version because it's rather difficult to produce lombok builds that are compatible on JDK9 without at least building with JDK9. Sorry about that.</fail> </target> @@ -254,14 +246,18 @@ the common tasks and can be called on to run the main aspects of all the sub-scr <classpath refid="build.path" /> </ivy:compile> - <ivy:compile destdir="build/lombok" source="1.9" target="1.9"> + <javac includeAntRuntime="false" source="1.9" target="1.9" destdir="build/lombok" modulepath="lib/moduleBuild"> <compilerarg value="-Xlint:none" /> <!-- The above is because javac9 warns about 'service interface provided but not exported or used', probably because lombok uses SPI internally, and uses the 'old' classpath discovery system for it. We're fine with this, hence, ignore this warning. --> <src path="src/core9" /> + </javac> + <mkdir dir="build/lombokMapstruct" /> + <javac includeAntRuntime="false" source="1.9" target="1.9" destdir="build/lombokMapstruct"> <src path="src/j9stubs" /> <!-- This includes org.mapstruct.ap.spi.AstModifyingAnnotationProcessor; putting this on the classpath doesn't work (needs to be internal or a module) so we just add it and then delete the class file for convenience. --> - </ivy:compile> - <delete file="build/lombok/org/mapstruct/ap/spi/AstModifyingAnnotationProcessor.class" /> + </javac> + <mkdir dir="build/lombok/secondaryLoading.SCL.lombok/org/mapstruct/ap/spi" /> + <move file="build/lombokMapstruct/org/mapstruct/ap/spi/AstModifyingAnnotationProcessor.class" tofile="build/lombok/secondaryLoading.SCL.lombok/org/mapstruct/ap/spi/AstModifyingAnnotationProcessor.SCL.lombok" /> <ivy:compile destdir="build/lombok/Class50" source="1.5" target="1.6" ecj="true" nowarn="true" includeSystemBootclasspath="true"> <bootclasspath location="build/stubs" /> diff --git a/buildScripts/ivy.xml b/buildScripts/ivy.xml index c1ddc6fd..4eed2fa6 100644 --- a/buildScripts/ivy.xml +++ b/buildScripts/ivy.xml @@ -5,6 +5,7 @@ <conf name="netbeansBuild" /> <conf name="buildBase" extends="netbeansBuild, javac6" /> <conf name="build" extends="buildBase, eclipseBuild" /> + <conf name="moduleBuild" /> <conf name="buildWithoutEclipse" extends="buildBase" /> <conf name="runtime" /> <conf name="test" extends="runtime" /> @@ -64,5 +65,7 @@ <dependency org="com.jcraft" name="jsch" rev="0.1.54" conf="supporters->default" /> <dependency org="com.rimerosolutions.ant" name="ant-git-tasks" rev="1.3.2" conf="supporters->default" /> <dependency org="org.slf4j" name="slf4j-simple" rev="1.6.1" conf="supporters->default" /> + + <dependency org="org.mapstruct" name="mapstruct-processor" rev="1.3.0.Beta1" conf="moduleBuild->default" /> </dependencies> </ivy-module> diff --git a/doc/changelog.markdown b/doc/changelog.markdown index 8166f3d1..6ba673ad 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -1,8 +1,16 @@ Lombok Changelog ---------------- -### v1.18.1 "Edgy Guinea Pig" +### v1.18.3 "Edgy Guinea Pig" +* FEATURE: The `@FieldNameConstants` feature has been completely redesigned. [Issue #1774](https://github.com/rzwitserloot/lombok/issues/1774) [FieldNameConstants documentation](https://projectlombok.org/features/experimental/FieldNameConstants) +* BUGFIX: When using lombok to compile modularized (`module-info.java`-style) code, if the module name has dots in it, it wouldn't work. [Issue #1808](https://github.com/rzwitserloot/lombok/issues/1808) +* BUGFIX: Errors about lombok not reading a module providing `org.mapstruct.ap.spi` when trying to use lombok in jigsaw-mode on JDK 11. [Issue #1806](https://github.com/rzwitserloot/lombok/issues/1806) + +### v1.18.2 (July 26th, 2018) +* BUGFIX: mapstruct + lombok in eclipse should hopefully work again. [Issue #1359](https://github.com/rzwitserloot/lombok/issues/1359) and [mapstruct issue #1159](https://github.com/mapstruct/mapstruct/issues/1159) * BUGFIX: Equals and hashCode again exclude transient fields by default. [Issue #1724](https://github.com/rzwitserloot/lombok/issues/1724) +* BUGFIX: Eclipse 'organize imports' feature (either explicitly, or if automatically triggered on saving via 'save actions') would remove the import for `lombok.var`. [Issue #1783](https://github.com/rzwitserloot/lombok/issues/1783) +* BUGFIX: Lombok and gradle v4.9 didn't work together; that's been fixed. [Issue #1716](https://github.com/rzwitserloot/lombok/issues/1716) and [gradle-apt-plugin issue #87](https://github.com/tbroyer/gradle-apt-plugin/issues/87) * FEATURE: You can now make builders for type hierarchies, using the new (experimental) `@SuperBuilder` annotation. Thanks for the contribution, Jan Rieke. [`@SuperBuilder` documentation](https://projectlombok.org/features/experimental/SuperBuilder) * FEATURE: `@NoArgsConstructor`, including forcing one with `lombok.config: lombok.noArgsConstructor.extraPrivate=true` now take any defaults set with `@Builder.Default` into account. [Issue #1347](https://github.com/rzwitserloot/lombok/issues/1347) diff --git a/jitpack.yml b/jitpack.yml new file mode 100644 index 00000000..334e7acc --- /dev/null +++ b/jitpack.yml @@ -0,0 +1,4 @@ +jdk: +- openjdk9 +before_install: +- export JAVA_HOME=/usr/lib/jvm/java-9-oracle diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java index f5134bbd..184ded27 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2017 The Project Lombok Authors. + * Copyright (C) 2013-2018 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,6 +39,13 @@ public class ConfigurationKeys { // ----- global ----- /** + * lombok configuration: {@code dangerousconfig.lombok.disable} = {@code true} | {@code false}. + * + * If {@code true}, lombok is disabled entirely. + */ + public static final ConfigurationKey<Boolean> LOMBOK_DISABLE = new ConfigurationKey<Boolean>("dangerousconfig.lombok.disable", "Disables lombok transformers. It does not flag any lombok mentions (so, @Cleanup silently does nothing), and does not disable patched operations in eclipse either. Don't use this unless you know what you're doing. (default: false).", true) {}; + + /** * lombok configuration: {@code lombok.addGeneratedAnnotation} = {@code true} | {@code false}. * * If {@code true}, lombok generates {@code @javax.annotation.Generated("lombok")} on all fields, methods, and types that are generated, unless {@code lombok.addJavaxGeneratedAnnotation} is set. @@ -522,18 +529,11 @@ public class ConfigurationKeys { public static final ConfigurationKey<FlagUsageType> FIELD_NAME_CONSTANTS_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.fieldNameConstants.flagUsage", "Emit a warning or error if @FieldNameConstants is used.") {}; /** - * lombok configuration: {@code lombok.fieldNameConstants.prefix} = <String: aJavaIdentifierPrefix> (Default: {@code PREFIX_}). + * lombok configuration: {@code lombok.fieldNameConstants.innerTypeName} = <String: AValidJavaTypeName> (Default: {@code Fields}). * * The names of the constants generated by {@code @FieldNameConstants} will be prefixed with this value. */ - public static final ConfigurationKey<String> FIELD_NAME_CONSTANTS_PREFIX = new ConfigurationKey<String>("lombok.fieldNameConstants.prefix", "names of constants generated by @FieldNameConstants will be prefixed with this value. (default: 'PREFIX_').") {}; - - /** - * lombok configuration: {@code lombok.fieldNameConstants.suffix} = <String: aJavaIdentifierPrefix> (Default: nothing). - * - * The names of the constants generated by {@code @FieldNameConstants} will be suffixed with this value. - */ - public static final ConfigurationKey<String> FIELD_NAME_CONSTANTS_SUFFIX = new ConfigurationKey<String>("lombok.fieldNameConstants.suffix", "names of constants generated by @FieldNameConstants will be suffixed with this value. (default: nothing).") {}; + public static final ConfigurationKey<String> FIELD_NAME_CONSTANTS_INNER_TYPE_NAME = new ConfigurationKey<String>("lombok.fieldNameConstants.innerTypeName", "The default name of the inner type generated by @FieldNameConstants. (default: 'Fields').") {}; // ----- Wither ----- diff --git a/src/core/lombok/bytecode/ClassFileMetaData.java b/src/core/lombok/bytecode/ClassFileMetaData.java index 0dc6a6c8..826eed83 100644 --- a/src/core/lombok/bytecode/ClassFileMetaData.java +++ b/src/core/lombok/bytecode/ClassFileMetaData.java @@ -116,23 +116,24 @@ public class ClassFileMetaData { int end = pos + size; // the resulting string might be smaller - StringBuilder result = new StringBuilder(size); + char[] result = new char[size]; + int length = 0; while (pos < end) { int first = (byteCode[pos++] & 0xFF); if (first < 0x80) { - result.append((char)first); + result[length++] = (char)first; } else if ((first & 0xE0) == 0xC0) { int x = (first & 0x1F) << 6; int y = (byteCode[pos++] & 0x3F); - result.append((char)(x | y)); + result[length++] = (char)(x | y); } else { int x = (first & 0x0F) << 12; int y = (byteCode[pos++] & 0x3F) << 6; int z = (byteCode[pos++] & 0x3F); - result.append((char)(x | y | z)); + result[length++] = (char)(x | y | z); } } - return result.toString(); + return new String(result, 0, length); } /** diff --git a/src/core/lombok/core/AST.java b/src/core/lombok/core/AST.java index 1142018f..fe7a4330 100644 --- a/src/core/lombok/core/AST.java +++ b/src/core/lombok/core/AST.java @@ -216,19 +216,19 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>, } } - private static final ConcurrentMap<Class<?>, Collection<FieldAccess>> fieldsOfASTClasses = new ConcurrentHashMap<Class<?>, Collection<FieldAccess>>(); + private static final ConcurrentMap<Class<?>, FieldAccess[]> fieldsOfASTClasses = new ConcurrentHashMap<Class<?>, FieldAccess[]>(); /** Returns FieldAccess objects for the stated class. Each field that contains objects of the kind returned by * {@link #getStatementTypes()}, either directly or inside of an array or java.util.collection (or array-of-arrays, * or collection-of-collections, et cetera), is returned. */ - protected Collection<FieldAccess> fieldsOf(Class<?> c) { - Collection<FieldAccess> fields = fieldsOfASTClasses.get(c); + protected FieldAccess[] fieldsOf(Class<?> c) { + FieldAccess[] fields = fieldsOfASTClasses.get(c); if (fields != null) return fields; - fields = new ArrayList<FieldAccess>(); - getFields(c, fields); - fieldsOfASTClasses.putIfAbsent(c, fields); + List<FieldAccess> fieldList = new ArrayList<FieldAccess>(); + getFields(c, fieldList); + fieldsOfASTClasses.putIfAbsent(c, fieldList.toArray(new FieldAccess[fieldList.size()])); return fieldsOfASTClasses.get(c); } diff --git a/src/core/lombok/core/AnnotationProcessor.java b/src/core/lombok/core/AnnotationProcessor.java index 04448ecb..293bfef6 100644 --- a/src/core/lombok/core/AnnotationProcessor.java +++ b/src/core/lombok/core/AnnotationProcessor.java @@ -49,7 +49,7 @@ import lombok.patcher.ClassRootFinder; @SupportedAnnotationTypes("*") public class AnnotationProcessor extends AbstractProcessor { - + private static String trace(Throwable t) { StringWriter w = new StringWriter(); t.printStackTrace(new PrintWriter(w, true)); @@ -65,42 +65,35 @@ public class AnnotationProcessor extends AbstractProcessor { private final List<ProcessorDescriptor> registered = Arrays.asList(new JavacDescriptor(), new EcjDescriptor()); private final List<ProcessorDescriptor> active = new ArrayList<ProcessorDescriptor>(); private final List<String> delayedWarnings = new ArrayList<String>(); - + /** * This method is a simplified version of {@link lombok.javac.apt.LombokProcessor.getJavacProcessingEnvironment} * It simply returns the processing environment, but in case of gradle incremental compilation, * the delegate ProcessingEnvironment of the gradle wrapper is returned. */ public static ProcessingEnvironment getJavacProcessingEnvironment(ProcessingEnvironment procEnv, List<String> delayedWarnings) { - ProcessingEnvironment javacProcEnv = tryRecursivelyObtainJavacProcessingEnvironment(procEnv); - - if (javacProcEnv == null) { - delayedWarnings.add("Can't get the delegate of the gradle IncrementalProcessingEnvironment."); - } - - return javacProcEnv; + return tryRecursivelyObtainJavacProcessingEnvironment(procEnv); } - + private static ProcessingEnvironment tryRecursivelyObtainJavacProcessingEnvironment(ProcessingEnvironment procEnv) { if (procEnv.getClass().getName().equals("com.sun.tools.javac.processing.JavacProcessingEnvironment")) { return procEnv; } - + for (Class<?> procEnvClass = procEnv.getClass(); procEnvClass != null; procEnvClass = procEnvClass.getSuperclass()) { try { Field field = procEnvClass.getDeclaredField("delegate"); field.setAccessible(true); Object delegate = field.get(procEnv); - + return tryRecursivelyObtainJavacProcessingEnvironment((ProcessingEnvironment) delegate); } catch (final Exception e) { // no valid delegate, try superclass } } - + return null; } - static class JavacDescriptor extends ProcessorDescriptor { private Processor processor; @@ -110,10 +103,13 @@ public class AnnotationProcessor extends AbstractProcessor { } @Override boolean want(ProcessingEnvironment procEnv, List<String> delayedWarnings) { + // do not run on ECJ as it may print warnings + if (procEnv.getClass().getName().startsWith("org.eclipse.jdt.")) return false; + ProcessingEnvironment javacProcEnv = getJavacProcessingEnvironment(procEnv, delayedWarnings); - + if (javacProcEnv == null) return false; - + try { ClassLoader classLoader = findAndPatchClassLoader(javacProcEnv); processor = (Processor) Class.forName("lombok.javac.apt.LombokProcessor", false, classLoader).newInstance(); @@ -209,7 +205,7 @@ public class AnnotationProcessor extends AbstractProcessor { for (TypeElement elem : annotations) { zeroElems = false; Name n = elem.getQualifiedName(); - if (n.length() > 7 && n.subSequence(0, 7).toString().equals("lombok.")) continue; + if (n.toString().startsWith("lombok.")) continue; onlyLombok = false; } diff --git a/src/core/lombok/core/LombokConfiguration.java b/src/core/lombok/core/LombokConfiguration.java index eb7b3d75..4a79c797 100644 --- a/src/core/lombok/core/LombokConfiguration.java +++ b/src/core/lombok/core/LombokConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2014 The Project Lombok Authors. + * Copyright (C) 2013-2018 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.core; +import java.net.URI; import java.util.Collections; import lombok.core.configuration.BubblingConfigurationResolver; @@ -44,7 +45,7 @@ public class LombokConfiguration { static { if (System.getProperty("lombok.disableConfig") != null) { configurationResolverFactory = new ConfigurationResolverFactory() { - @Override public ConfigurationResolver createResolver(AST<?, ?, ?> ast) { + @Override public ConfigurationResolver createResolver(URI sourceLocation) { return NULL_RESOLVER; } }; @@ -63,13 +64,17 @@ public class LombokConfiguration { } static <T> T read(ConfigurationKey<T> key, AST<?, ?, ?> ast) { - return configurationResolverFactory.createResolver(ast).resolve(key); + return configurationResolverFactory.createResolver(ast.getAbsoluteFileLocation()).resolve(key); + } + + public static <T> T read(ConfigurationKey<T> key, URI sourceLocation) { + return configurationResolverFactory.createResolver(sourceLocation).resolve(key); } private static ConfigurationResolverFactory createFileSystemBubblingResolverFactory() { return new ConfigurationResolverFactory() { - @Override public ConfigurationResolver createResolver(AST<?, ?, ?> ast) { - return new BubblingConfigurationResolver(cache.sourcesForJavaFile(ast.getAbsoluteFileLocation(), ConfigurationProblemReporter.CONSOLE)); + @Override public ConfigurationResolver createResolver(URI sourceLocation) { + return new BubblingConfigurationResolver(cache.sourcesForJavaFile(sourceLocation, ConfigurationProblemReporter.CONSOLE)); } }; } diff --git a/src/core/lombok/core/LombokInternalAliasing.java b/src/core/lombok/core/LombokInternalAliasing.java index 3dc1bfa2..c1089580 100644 --- a/src/core/lombok/core/LombokInternalAliasing.java +++ b/src/core/lombok/core/LombokInternalAliasing.java @@ -36,10 +36,8 @@ public class LombokInternalAliasing { */ 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; + String ret = ALIASES.get(in); + return ret == null ? in : ret; } static { diff --git a/src/core/lombok/core/LombokNode.java b/src/core/lombok/core/LombokNode.java index d6708956..5a0842bc 100644 --- a/src/core/lombok/core/LombokNode.java +++ b/src/core/lombok/core/LombokNode.java @@ -40,7 +40,6 @@ import lombok.core.AST.Kind; * For example, JCTree for javac, and ASTNode for Eclipse. */ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, L, N>, N> implements DiagnosticsReceiver { - protected final A ast; protected final Kind kind; protected final N node; protected LombokImmutableList<L> children; @@ -59,8 +58,7 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, * @param kind The kind of node represented by this object. */ @SuppressWarnings("unchecked") - protected LombokNode(A ast, N node, List<L> children, Kind kind) { - this.ast = ast; + protected LombokNode(N node, List<L> children, Kind kind) { this.kind = kind; this.node = node; this.children = children != null ? LombokImmutableList.copyOf(children) : LombokImmutableList.<L>of(); @@ -72,9 +70,7 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, this.isStructurallySignificant = calculateIsStructurallySignificant(null); } - public A getAst() { - return ast; - } + public abstract A getAst(); /** {@inheritDoc} */ @Override public String toString() { @@ -88,7 +84,7 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, * @see AST#getPackageDeclaration() */ public String getPackageDeclaration() { - return ast.getPackageDeclaration(); + return getAst().getPackageDeclaration(); } /** @@ -97,7 +93,7 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, * @see AST#getImportList() */ public ImportList getImportList() { - return ast.getImportList(); + return getAst().getImportList(); } /** @@ -111,7 +107,7 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, * @see AST#get(Object) */ public L getNodeFor(N obj) { - return ast.get(obj); + return getAst().get(obj); } /** @@ -187,7 +183,7 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, * @see AST#getLatestJavaSpecSupported() */ public int getLatestJavaSpecSupported() { - return ast.getLatestJavaSpecSupported(); + return getAst().getLatestJavaSpecSupported(); } /** @@ -196,7 +192,7 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, * @see AST#getSourceVersion() */ public int getSourceVersion() { - return ast.getSourceVersion(); + return getAst().getSourceVersion(); } /** @@ -205,7 +201,7 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, * @see AST#top() */ public L top() { - return ast.top(); + return getAst().top(); } /** @@ -214,7 +210,7 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, * @see AST#getFileName() */ public String getFileName() { - return ast.getFileName(); + return getAst().getFileName(); } /** @@ -224,8 +220,8 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, */ @SuppressWarnings({"unchecked"}) public L add(N newChild, Kind newChildKind) { - ast.setChanged(); - L n = ast.buildTree(newChild, newChildKind); + getAst().setChanged(); + L n = getAst().buildTree(newChild, newChildKind); if (n == null) return null; n.parent = (L) this; children = children.append(n); @@ -242,20 +238,20 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A, Map<N, L> oldNodes = new IdentityHashMap<N, L>(); gatherAndRemoveChildren(oldNodes); - L newNode = ast.buildTree(get(), kind); + L newNode = getAst().buildTree(get(), kind); - ast.setChanged(); + getAst().setChanged(); - ast.replaceNewWithExistingOld(oldNodes, newNode); + getAst().replaceNewWithExistingOld(oldNodes, newNode); } @SuppressWarnings({"unchecked", "rawtypes"}) private void gatherAndRemoveChildren(Map<N, L> map) { for (LombokNode child : children) child.gatherAndRemoveChildren(map); - ast.identityDetector.remove(get()); + getAst().identityDetector.remove(get()); map.put(get(), (L) this); children = LombokImmutableList.of(); - ast.getNodeMap().remove(get()); + getAst().getNodeMap().remove(get()); } /** @@ -264,7 +260,7 @@ 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. */ public void removeChild(L child) { - ast.setChanged(); + getAst().setChanged(); children = children.removeElement(child); } diff --git a/src/core/lombok/core/Version.java b/src/core/lombok/core/Version.java index 70af48e0..4b119873 100644 --- a/src/core/lombok/core/Version.java +++ b/src/core/lombok/core/Version.java @@ -30,7 +30,7 @@ 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 = "1.18.1"; + private static final String VERSION = "1.18.3"; private static final String RELEASE_NAME = "Edgy Guinea Pig"; // private static final String RELEASE_NAME = "Envious Ferret"; diff --git a/src/core/lombok/core/configuration/ConfigurationApp.java b/src/core/lombok/core/configuration/ConfigurationApp.java index efe57e38..9cfec2e7 100644 --- a/src/core/lombok/core/configuration/ConfigurationApp.java +++ b/src/core/lombok/core/configuration/ConfigurationApp.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Project Lombok Authors. + * Copyright (C) 2014-2018 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 @@ -118,7 +118,7 @@ public class ConfigurationApp extends LombokApp { boolean verbose = args.verbose; if (args.generate) { - return generate(keys, verbose); + return generate(keys, verbose, !args.key.isEmpty()); } return display(keys, verbose, args.paths, !args.key.isEmpty()); @@ -130,8 +130,9 @@ public class ConfigurationApp extends LombokApp { return this; } - public int generate(Collection<ConfigurationKey<?>> keys, boolean verbose) { + public int generate(Collection<ConfigurationKey<?>> keys, boolean verbose, boolean explicit) { for (ConfigurationKey<?> key : keys) { + if (!explicit && key.isHidden()) continue; String keyName = key.getKeyName(); ConfigurationDataType type = key.getType(); String description = key.getDescription(); diff --git a/src/core/lombok/core/configuration/ConfigurationKey.java b/src/core/lombok/core/configuration/ConfigurationKey.java index d46a70b0..18075190 100644 --- a/src/core/lombok/core/configuration/ConfigurationKey.java +++ b/src/core/lombok/core/configuration/ConfigurationKey.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2014 The Project Lombok Authors. + * Copyright (C) 2013-2018 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 @@ -43,13 +43,19 @@ public abstract class ConfigurationKey<T> { private final String keyName; private final String description; private final ConfigurationDataType type; + private final boolean hidden; public ConfigurationKey(String keyName, String description) { + this(keyName, description, false); + } + + public ConfigurationKey(String keyName, String description, boolean hidden) { this.keyName = checkName(keyName); @SuppressWarnings("unchecked") ConfigurationDataType type = ConfigurationDataType.toDataType((Class<? extends ConfigurationKey<?>>)getClass()); this.type = type; this.description = description; + this.hidden = hidden; registerKey(keyName, this); } @@ -65,6 +71,10 @@ public abstract class ConfigurationKey<T> { return type; } + public final boolean isHidden() { + return hidden; + } + @Override public String toString() { return keyName + " (" + type + "): " + description; } diff --git a/src/core/lombok/core/configuration/ConfigurationResolverFactory.java b/src/core/lombok/core/configuration/ConfigurationResolverFactory.java index 83b58c2f..b640b271 100644 --- a/src/core/lombok/core/configuration/ConfigurationResolverFactory.java +++ b/src/core/lombok/core/configuration/ConfigurationResolverFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Project Lombok Authors. + * Copyright (C) 2014-2018 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,8 +21,8 @@ */ package lombok.core.configuration; -import lombok.core.AST; +import java.net.URI; public interface ConfigurationResolverFactory { - ConfigurationResolver createResolver(AST<?, ?, ?> ast); + ConfigurationResolver createResolver(URI sourceLocation); } diff --git a/src/core/lombok/core/configuration/StringConfigurationSource.java b/src/core/lombok/core/configuration/StringConfigurationSource.java index dd2f0319..abf6eea0 100644 --- a/src/core/lombok/core/configuration/StringConfigurationSource.java +++ b/src/core/lombok/core/configuration/StringConfigurationSource.java @@ -67,7 +67,6 @@ public class StringConfigurationSource implements ConfigurationSource { list.add(new ListModification(value, add)); } }); - return new StringConfigurationSource(values); } diff --git a/src/core/lombok/eclipse/EclipseAST.java b/src/core/lombok/eclipse/EclipseAST.java index dc2c9843..7cd2e400 100644 --- a/src/core/lombok/eclipse/EclipseAST.java +++ b/src/core/lombok/eclipse/EclipseAST.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2013 The Project Lombok Authors. + * Copyright (C) 2009-2018 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 @@ -73,16 +73,20 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { private static final URI NOT_CALCULATED_MARKER = URI.create("https://projectlombok.org/not/calculated"); private URI memoizedAbsoluteFileLocation = NOT_CALCULATED_MARKER; + public static URI getAbsoluteFileLocation(CompilationUnitDeclaration ast) { + return getAbsoluteFileLocation0(ast); + } + public URI getAbsoluteFileLocation() { if (memoizedAbsoluteFileLocation != NOT_CALCULATED_MARKER) return memoizedAbsoluteFileLocation; - memoizedAbsoluteFileLocation = getAbsoluteFileLocation0(); + memoizedAbsoluteFileLocation = getAbsoluteFileLocation0(this.compilationUnitDeclaration); return memoizedAbsoluteFileLocation; } /** This is the call, but we wrapped it to memoize this. */ - private URI getAbsoluteFileLocation0() { - String fileName = getFileName(); + private static URI getAbsoluteFileLocation0(CompilationUnitDeclaration ast) { + String fileName = toFileName(ast); if (fileName != null && (fileName.startsWith("file:") || fileName.startsWith("sourcecontrol:"))) { // Some exotic build systems get real fancy with filenames. Known culprits: // The 'jazz' source control system _probably_ (not confirmed yet) uses sourcecontrol://jazz: urls. diff --git a/src/core/lombok/eclipse/EclipseASTAdapter.java b/src/core/lombok/eclipse/EclipseASTAdapter.java index 2a78c270..61807fff 100644 --- a/src/core/lombok/eclipse/EclipseASTAdapter.java +++ b/src/core/lombok/eclipse/EclipseASTAdapter.java @@ -36,6 +36,9 @@ import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; * has been implemented with an empty body. Override whichever methods you need. */ public abstract class EclipseASTAdapter implements EclipseASTVisitor { + + private final boolean deferUntilPostDiet = getClass().isAnnotationPresent(DeferUntilPostDiet.class); + /** {@inheritDoc} */ public void visitCompilationUnit(EclipseNode top, CompilationUnitDeclaration unit) {} @@ -98,4 +101,8 @@ public abstract class EclipseASTAdapter implements EclipseASTVisitor { /** {@inheritDoc} */ public void endVisitStatement(EclipseNode statementNode, Statement statement) {} + + public boolean isDeferUntilPostDiet() { + return deferUntilPostDiet ; + } } diff --git a/src/core/lombok/eclipse/EclipseASTVisitor.java b/src/core/lombok/eclipse/EclipseASTVisitor.java index f5b49cbb..b2fd4b2f 100644 --- a/src/core/lombok/eclipse/EclipseASTVisitor.java +++ b/src/core/lombok/eclipse/EclipseASTVisitor.java @@ -26,7 +26,9 @@ import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.io.PrintStream; import java.lang.reflect.Modifier; +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.Block; @@ -38,6 +40,7 @@ import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeReference; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; /** * Implement so you can ask any EclipseAST.Node to traverse depth-first through all children, @@ -199,8 +202,82 @@ public interface EclipseASTVisitor { print("</CUD>"); } + private String printFlags(int flags, ASTNode node) { + StringBuilder out = new StringBuilder(); + if ((flags & ClassFileConstants.AccPublic) != 0) { + flags &= ~ClassFileConstants.AccPublic; + out.append("public "); + } + if ((flags & ClassFileConstants.AccPrivate) != 0) { + flags &= ~ClassFileConstants.AccPrivate; + out.append("private "); + } + if ((flags & ClassFileConstants.AccProtected) != 0) { + flags &= ~ClassFileConstants.AccProtected; + out.append("protected "); + } + if ((flags & ClassFileConstants.AccStatic) != 0) { + flags &= ~ClassFileConstants.AccStatic; + out.append("static "); + } + if ((flags & ClassFileConstants.AccFinal) != 0) { + flags &= ~ClassFileConstants.AccFinal; + out.append("final "); + } + if ((flags & ClassFileConstants.AccSynchronized) != 0) { + flags &= ~ClassFileConstants.AccSynchronized; + out.append("synchronized "); + } + if ((flags & ClassFileConstants.AccNative) != 0) { + flags &= ~ClassFileConstants.AccNative; + out.append("native "); + } + if ((flags & ClassFileConstants.AccInterface) != 0) { + flags &= ~ClassFileConstants.AccInterface; + out.append("interface "); + } + if ((flags & ClassFileConstants.AccAbstract) != 0) { + flags &= ~ClassFileConstants.AccAbstract; + out.append("abstract "); + } + if ((flags & ClassFileConstants.AccStrictfp) != 0) { + flags &= ~ClassFileConstants.AccStrictfp; + out.append("strictfp "); + } + if ((flags & ClassFileConstants.AccSynthetic) != 0) { + flags &= ~ClassFileConstants.AccSynthetic; + out.append("synthetic "); + } + if ((flags & ClassFileConstants.AccAnnotation) != 0) { + flags &= ~ClassFileConstants.AccAnnotation; + out.append("annotation "); + } + if ((flags & ClassFileConstants.AccEnum) != 0) { + flags &= ~ClassFileConstants.AccEnum; + out.append("enum "); + } + + if ((flags & ClassFileConstants.AccVolatile) != 0) { + flags &= ~ClassFileConstants.AccVolatile; + if (node instanceof FieldDeclaration) out.append("volatile "); + else out.append("volatile/bridge "); + } + if ((flags & ClassFileConstants.AccTransient) != 0) { + flags &= ~ClassFileConstants.AccTransient; + if (node instanceof Argument) out.append("varargs "); + else if (node instanceof FieldDeclaration) out.append("transient "); + else out.append("transient/varargs "); + } + + if (flags != 0) { + out.append(String.format(" 0x%08X ", flags)); + } + + return out.toString().trim(); + } + public void visitType(EclipseNode node, TypeDeclaration type) { - print("<TYPE %s%s%s>", str(type.name), isGenerated(type) ? " (GENERATED)" : "", position(node)); + print("<TYPE %s%s%s> %s", str(type.name), isGenerated(type) ? " (GENERATED)" : "", position(node), printFlags(type.modifiers, type)); indent++; if (printContent) { print("%s", type); @@ -239,8 +316,8 @@ public interface EclipseASTVisitor { } public void visitField(EclipseNode node, FieldDeclaration field) { - print("<FIELD%s %s %s = %s%s>", isGenerated(field) ? " (GENERATED)" : "", - str(field.type), str(field.name), field.initialization, position(node)); + print("<FIELD%s %s %s = %s%s> %s", isGenerated(field) ? " (GENERATED)" : "", + str(field.type), str(field.name), field.initialization, position(node), printFlags(field.modifiers, field)); indent++; if (printContent) { if (field.initialization != null) print("%s", field.initialization); @@ -260,9 +337,13 @@ public interface EclipseASTVisitor { public void visitMethod(EclipseNode node, AbstractMethodDeclaration method) { String type = method instanceof ConstructorDeclaration ? "CONSTRUCTOR" : "METHOD"; - print("<%s %s: %s%s%s>", type, str(method.selector), method.statements != null ? "filled" : "blank", - isGenerated(method) ? " (GENERATED)" : "", position(node)); + print("<%s %s: %s%s%s> %s", type, str(method.selector), method.statements != null ? ("filled(" + method.statements.length + ")") : "blank", + isGenerated(method) ? " (GENERATED)" : "", position(node), printFlags(method.modifiers, method)); indent++; + if (method instanceof ConstructorDeclaration) { + ConstructorDeclaration cd = (ConstructorDeclaration) method; + print("--> constructorCall: %s", cd.constructorCall == null ? "-NONE-" : cd.constructorCall); + } if (printContent) { if (method.statements != null) print("%s", method); disablePrinting++; @@ -281,7 +362,8 @@ public interface EclipseASTVisitor { } public void visitMethodArgument(EclipseNode node, Argument arg, AbstractMethodDeclaration method) { - print("<METHODARG%s %s %s = %s%s>", isGenerated(arg) ? " (GENERATED)" : "", str(arg.type), str(arg.name), arg.initialization, position(node)); + print("<METHODARG%s %s %s = %s%s> %s", isGenerated(arg) ? " (GENERATED)" : "", + str(arg.type), str(arg.name), arg.initialization, position(node), printFlags(arg.modifiers, arg)); indent++; } @@ -295,7 +377,8 @@ public interface EclipseASTVisitor { } public void visitLocal(EclipseNode node, LocalDeclaration local) { - print("<LOCAL%s %s %s = %s%s>", isGenerated(local) ? " (GENERATED)" : "", str(local.type), str(local.name), local.initialization, position(node)); + print("<LOCAL%s %s %s = %s%s> %s", isGenerated(local) ? " (GENERATED)" : "", + str(local.type), str(local.name), local.initialization, position(node), printFlags(local.modifiers, local)); indent++; } @@ -310,6 +393,14 @@ public interface EclipseASTVisitor { public void visitStatement(EclipseNode node, Statement statement) { print("<%s%s%s>", statement.getClass(), isGenerated(statement) ? " (GENERATED)" : "", position(node)); + if (statement instanceof AllocationExpression) { + AllocationExpression alloc = (AllocationExpression) statement; + print(" --> arguments: %s", alloc.arguments == null ? "NULL" : alloc.arguments.length); + print(" --> genericTypeArguments: %s", alloc.genericTypeArguments == null ? "NULL" : alloc.genericTypeArguments.length); + print(" --> typeArguments: %s", alloc.typeArguments == null ? "NULL" : alloc.typeArguments.length); + print(" --> enumConstant: %s", alloc.enumConstant); + print(" --> inferredReturnType: %s", alloc.inferredReturnType); + } indent++; print("%s", statement); } @@ -325,5 +416,11 @@ public interface EclipseASTVisitor { int end = node.get().sourceEnd(); return String.format(" [%d, %d]", start, end); } + + public boolean isDeferUntilPostDiet() { + return false; + } } + + boolean isDeferUntilPostDiet(); } diff --git a/src/core/lombok/eclipse/EclipseNode.java b/src/core/lombok/eclipse/EclipseNode.java index 4db1d38d..1738c770 100644 --- a/src/core/lombok/eclipse/EclipseNode.java +++ b/src/core/lombok/eclipse/EclipseNode.java @@ -44,16 +44,22 @@ import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; * Eclipse specific version of the LombokNode class. */ public class EclipseNode extends lombok.core.LombokNode<EclipseAST, EclipseNode, ASTNode> { + private EclipseAST ast; /** {@inheritDoc} */ EclipseNode(EclipseAST ast, ASTNode node, List<EclipseNode> children, Kind kind) { - super(ast, node, children, kind); + super(node, children, kind); + this.ast = ast; } + @Override + public EclipseAST getAst() { + return ast; + } /** * Visits this node and all child nodes depth-first, calling the provided visitor's visit methods. */ public void traverse(EclipseASTVisitor visitor) { - if (!this.isCompleteParse() && visitor.getClass().isAnnotationPresent(DeferUntilPostDiet.class)) return; + if (visitor.isDeferUntilPostDiet() && !isCompleteParse()) return; switch (getKind()) { case COMPILATION_UNIT: diff --git a/src/core/lombok/eclipse/HandlerLibrary.java b/src/core/lombok/eclipse/HandlerLibrary.java index 07c6f97b..0e72fb38 100644 --- a/src/core/lombok/eclipse/HandlerLibrary.java +++ b/src/core/lombok/eclipse/HandlerLibrary.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 The Project Lombok Authors. + * Copyright (C) 2009-2018 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 @@ -212,21 +212,25 @@ public class HandlerLibrary { * @param ast The Compilation Unit that contains the Annotation AST Node. * @param annotationNode The Lombok AST Node representing the Annotation AST Node. * @param annotation 'node.get()' - convenience parameter. + * @param priority current prioritiy + * @return the priority we want to run - MAX_VALUE means never */ - public void handleAnnotation(CompilationUnitDeclaration ast, EclipseNode annotationNode, org.eclipse.jdt.internal.compiler.ast.Annotation annotation, long priority) { + public long handleAnnotation(CompilationUnitDeclaration ast, EclipseNode annotationNode, org.eclipse.jdt.internal.compiler.ast.Annotation annotation, long priority) { TypeResolver resolver = new TypeResolver(annotationNode.getImportList()); TypeReference rawType = annotation.type; - if (rawType == null) return; + if (rawType == null) return Long.MAX_VALUE; String fqn = resolver.typeRefToFullyQualifiedName(annotationNode, typeLibrary, toQualifiedName(annotation.type.getTypeName())); - if (fqn == null) return; + if (fqn == null) return Long.MAX_VALUE; AnnotationHandlerContainer<?> container = annotationHandlers.get(fqn); - if (container == null) return; - if (priority != container.getPriority()) return; + if (container == null) return Long.MAX_VALUE; + + if (priority < container.getPriority()) return container.getPriority(); // we want to run at this priority + if (priority > container.getPriority()) return Long.MAX_VALUE; // it's over- we do not want to run again if (!annotationNode.isCompleteParse() && container.deferUntilPostDiet()) { if (needsHandling(annotation)) container.preHandle(annotation, annotationNode); - return; + return Long.MAX_VALUE; } try { @@ -236,13 +240,16 @@ public class HandlerLibrary { } catch (Throwable t) { error(ast, String.format("Lombok annotation handler %s failed", container.handler.getClass()), t); } + return Long.MAX_VALUE; } /** * Will call all registered {@link EclipseASTVisitor} instances. */ - public void callASTVisitors(EclipseAST ast, long priority, boolean isCompleteParse) { + public long callASTVisitors(EclipseAST ast, long priority, boolean isCompleteParse) { + long nearestPriority = Long.MAX_VALUE; for (VisitorContainer container : visitorHandlers) { + if (priority < container.getPriority()) nearestPriority = Math.min(container.getPriority(), nearestPriority); if (!isCompleteParse && container.deferUntilPostDiet()) continue; if (priority != container.getPriority()) continue; try { @@ -252,5 +259,6 @@ public class HandlerLibrary { String.format("Lombok visitor handler %s failed", container.visitor.getClass()), t); } } + return nearestPriority; } } diff --git a/src/core/lombok/eclipse/TransformEclipseAST.java b/src/core/lombok/eclipse/TransformEclipseAST.java index 683465c9..323fc171 100644 --- a/src/core/lombok/eclipse/TransformEclipseAST.java +++ b/src/core/lombok/eclipse/TransformEclipseAST.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2012 The Project Lombok Authors. + * Copyright (C) 2009-2018 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,8 @@ import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.lang.reflect.Field; +import lombok.ConfigurationKeys; +import lombok.core.LombokConfiguration; import lombok.core.debug.DebugSnapshotStore; import lombok.core.debug.HistogramTracker; import lombok.patcher.Symbols; @@ -143,6 +145,8 @@ public class TransformEclipseAST { // Do NOT abort if (ast.bits & ASTNode.HasAllMethodBodies) != 0 - that doesn't work. + if (Boolean.TRUE.equals(LombokConfiguration.read(ConfigurationKeys.LOMBOK_DISABLE, EclipseAST.getAbsoluteFileLocation(ast)))) return; + try { DebugSnapshotStore.INSTANCE.snapshot(ast, "transform entry"); long histoToken = lombokTracker == null ? 0L : lombokTracker.start(); @@ -182,42 +186,54 @@ public class TransformEclipseAST { * then handles any PrintASTs. */ public void go() { + long nextPriority = Long.MIN_VALUE; for (Long d : handlers.getPriorities()) { - ast.traverse(new AnnotationVisitor(d)); - handlers.callASTVisitors(ast, d, ast.isCompleteParse()); + if (nextPriority > d) continue; + AnnotationVisitor visitor = new AnnotationVisitor(d); + ast.traverse(visitor); + // if no visitor interested for this AST, nextPriority would be MAX_VALUE and we bail out immediatetly + nextPriority = visitor.getNextPriority(); + nextPriority = Math.min(nextPriority, handlers.callASTVisitors(ast, d, ast.isCompleteParse())); } } private static class AnnotationVisitor extends EclipseASTAdapter { private final long priority; + // this is the next priority we continue to visit. + // Long.MAX_VALUE means never. Each visit method will potentially reduce the next priority + private long nextPriority = Long.MAX_VALUE; public AnnotationVisitor(long priority) { this.priority = priority; } + public long getNextPriority() { + return nextPriority; + } + @Override public void visitAnnotationOnField(FieldDeclaration field, EclipseNode annotationNode, Annotation annotation) { CompilationUnitDeclaration top = (CompilationUnitDeclaration) annotationNode.top().get(); - handlers.handleAnnotation(top, annotationNode, annotation, priority); + nextPriority = Math.min(nextPriority, handlers.handleAnnotation(top, annotationNode, annotation, priority)); } @Override public void visitAnnotationOnMethodArgument(Argument arg, AbstractMethodDeclaration method, EclipseNode annotationNode, Annotation annotation) { CompilationUnitDeclaration top = (CompilationUnitDeclaration) annotationNode.top().get(); - handlers.handleAnnotation(top, annotationNode, annotation, priority); + nextPriority = Math.min(nextPriority, handlers.handleAnnotation(top, annotationNode, annotation, priority)); } @Override public void visitAnnotationOnLocal(LocalDeclaration local, EclipseNode annotationNode, Annotation annotation) { CompilationUnitDeclaration top = (CompilationUnitDeclaration) annotationNode.top().get(); - handlers.handleAnnotation(top, annotationNode, annotation, priority); + nextPriority = Math.min(nextPriority, handlers.handleAnnotation(top, annotationNode, annotation, priority)); } @Override public void visitAnnotationOnMethod(AbstractMethodDeclaration method, EclipseNode annotationNode, Annotation annotation) { CompilationUnitDeclaration top = (CompilationUnitDeclaration) annotationNode.top().get(); - handlers.handleAnnotation(top, annotationNode, annotation, priority); + nextPriority = Math.min(nextPriority, handlers.handleAnnotation(top, annotationNode, annotation, priority)); } @Override public void visitAnnotationOnType(TypeDeclaration type, EclipseNode annotationNode, Annotation annotation) { CompilationUnitDeclaration top = (CompilationUnitDeclaration) annotationNode.top().get(); - handlers.handleAnnotation(top, annotationNode, annotation, priority); + nextPriority = Math.min(nextPriority, handlers.handleAnnotation(top, annotationNode, annotation, priority)); } } } diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 2dce285c..87df6d1b 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -24,6 +24,7 @@ package lombok.eclipse.handlers; import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.Eclipse.*; import static lombok.eclipse.EclipseAugments.*; +import static lombok.eclipse.handlers.EclipseHandlerUtil.setGeneratedBy; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -1862,4 +1863,29 @@ public class EclipseHandlerUtil { String p = typeDecl.superclass.toString(); return p.equals("Object") || p.equals("java.lang.Object"); } + + public static NameReference generateQualifiedNameRef(ASTNode source, char[]... varNames) { + int pS = source.sourceStart, pE = source.sourceEnd; + long p = (long)pS << 32 | pE; + + NameReference ref; + + if (varNames.length > 1) ref = new QualifiedNameReference(varNames, new long[varNames.length], pS, pE); + else ref = new SingleNameReference(varNames[0], p); + setGeneratedBy(ref, source); + return ref; + } + + public static TypeReference generateQualifiedTypeRef(ASTNode source, char[]... varNames) { + int pS = source.sourceStart, pE = source.sourceEnd; + long p = (long)pS << 32 | pE; + + TypeReference ref; + + long[] poss = Eclipse.poss(source, varNames.length); + if (varNames.length > 1) ref = new QualifiedTypeReference(varNames, poss); + else ref = new SingleTypeReference(varNames[0], p); + setGeneratedBy(ref, source); + return ref; + } } diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index e1b1af26..9a069c58 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -40,6 +40,7 @@ import org.eclipse.jdt.internal.compiler.ast.Assignment; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression; import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; +import org.eclipse.jdt.internal.compiler.ast.EqualExpression; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FalseLiteral; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; @@ -47,6 +48,7 @@ import org.eclipse.jdt.internal.compiler.ast.FieldReference; import org.eclipse.jdt.internal.compiler.ast.IfStatement; import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.NullLiteral; import org.eclipse.jdt.internal.compiler.ast.OperatorIds; import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; @@ -146,6 +148,8 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } @Override public void handle(AnnotationValues<Builder> annotation, Annotation ast, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder"); + long p = (long) ast.sourceStart << 32 | ast.sourceEnd; Builder builderInstance = annotation.getInstance(); @@ -490,6 +494,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } } + private static final char[] EMPTY_LIST = "emptyList".toCharArray(); private MethodDeclaration generateToBuilderMethod(String methodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, List<BuilderFieldData> builderFields, boolean fluent, ASTNode source) { // return new ThingieBuilder<A, B>().setA(this.a).setB(this.b); @@ -509,19 +514,34 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { for (BuilderFieldData bfd : builderFields) { char[] setterName = fluent ? bfd.name : HandlerUtil.buildAccessorName("set", new String(bfd.name)).toCharArray(); MessageSend ms = new MessageSend(); + Expression[] tgt = new Expression[bfd.singularData == null ? 1 : 2]; + if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) { char[] fieldName = bfd.obtainVia == null ? bfd.rawName : bfd.obtainVia.field().toCharArray(); - FieldReference fr = new FieldReference(fieldName, 0); - fr.receiver = new ThisReference(0, 0); - ms.arguments = new Expression[] {fr}; + for (int i = 0; i < tgt.length; i++) { + FieldReference fr = new FieldReference(fieldName, 0); + fr.receiver = new ThisReference(0, 0); + tgt[i] = fr; + } } else { String obtainName = bfd.obtainVia.method(); boolean obtainIsStatic = bfd.obtainVia.isStatic(); - MessageSend obtainExpr = new MessageSend(); - obtainExpr.receiver = obtainIsStatic ? new SingleNameReference(type.getName().toCharArray(), 0) : new ThisReference(0, 0); - obtainExpr.selector = obtainName.toCharArray(); - if (obtainIsStatic) obtainExpr.arguments = new Expression[] {new ThisReference(0, 0)}; - ms.arguments = new Expression[] {obtainExpr}; + for (int i = 0; i < tgt.length; i++) { + MessageSend obtainExpr = new MessageSend(); + obtainExpr.receiver = obtainIsStatic ? new SingleNameReference(type.getName().toCharArray(), 0) : new ThisReference(0, 0); + obtainExpr.selector = obtainName.toCharArray(); + if (obtainIsStatic) obtainExpr.arguments = new Expression[] {new ThisReference(0, 0)}; + tgt[i] = obtainExpr; + } + } + if (bfd.singularData == null) { + ms.arguments = tgt; + } else { + Expression ifNull = new EqualExpression(tgt[0], new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL); + MessageSend emptyList = new MessageSend(); + emptyList.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA, TypeConstants.UTIL, "Collections".toCharArray()); + emptyList.selector = EMPTY_LIST; + ms.arguments = new Expression[] {new ConditionalExpression(ifNull, emptyList, tgt[1])}; } ms.receiver = receiver; ms.selector = setterName; diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java index c99b9b5f..6945e5d9 100644 --- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java @@ -69,7 +69,6 @@ import org.eclipse.jdt.internal.compiler.ast.NullLiteral; import org.eclipse.jdt.internal.compiler.ast.OperatorIds; import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; -import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; @@ -819,29 +818,4 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH expr.sourceStart = pS; expr.sourceEnd = pE; return expr; } - - public NameReference generateQualifiedNameRef(ASTNode source, char[]... varNames) { - int pS = source.sourceStart, pE = source.sourceEnd; - long p = (long)pS << 32 | pE; - - NameReference ref; - - if (varNames.length > 1) ref = new QualifiedNameReference(varNames, new long[varNames.length], pS, pE); - else ref = new SingleNameReference(varNames[0], p); - setGeneratedBy(ref, source); - return ref; - } - - public TypeReference generateQualifiedTypeRef(ASTNode source, char[]... varNames) { - int pS = source.sourceStart, pE = source.sourceEnd; - long p = (long)pS << 32 | pE; - - TypeReference ref; - - long[] poss = Eclipse.poss(source, varNames.length); - if (varNames.length > 1) ref = new QualifiedTypeReference(varNames, poss); - else ref = new SingleTypeReference(varNames[0], p); - setGeneratedBy(ref, source); - return ref; - } } diff --git a/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java index c3a28f7f..15650490 100644 --- a/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java +++ b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java @@ -24,32 +24,37 @@ package lombok.eclipse.handlers; import static lombok.core.handlers.HandlerUtil.handleExperimentalFlagUsage; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; -import java.lang.reflect.Modifier; -import java.util.Collection; +import java.util.ArrayList; +import java.util.List; import lombok.AccessLevel; import lombok.ConfigurationKeys; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; -import lombok.core.handlers.HandlerUtil; import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import lombok.experimental.FieldNameConstants; import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.Clinit; +import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.StringLiteral; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.mangosdk.spi.ProviderFor; @ProviderFor(EclipseAnnotationHandler.class) public class HandleFieldNameConstants extends EclipseAnnotationHandler<FieldNameConstants> { - public void generateFieldNameConstantsForType(EclipseNode typeNode, EclipseNode errorNode, AccessLevel level, String prefix, String suffix) { + public void generateFieldNameConstantsForType(EclipseNode typeNode, EclipseNode errorNode, AccessLevel level, boolean asEnum, String innerTypeName, boolean onlyExplicit) { TypeDeclaration typeDecl = null; if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); @@ -57,22 +62,29 @@ public class HandleFieldNameConstants extends EclipseAnnotationHandler<FieldName boolean notAClass = (modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0; if (typeDecl == null || notAClass) { - errorNode.addError("@FieldNameConstants is only supported on a class, an enum, or a field."); + errorNode.addError("@FieldNameConstants is only supported on a class or an enum."); return; } + List<EclipseNode> qualified = new ArrayList<EclipseNode>(); + for (EclipseNode field : typeNode.down()) { - if (fieldQualifiesForFieldNameConstantsGeneration(field)) generateFieldNameConstantsForField(field, errorNode.get(), level, prefix, suffix); + if (fieldQualifiesForFieldNameConstantsGeneration(field, onlyExplicit)) qualified.add(field); + } + + if (qualified.isEmpty()) { + errorNode.addWarning("No fields qualify for @FieldNameConstants, therefore this annotation does nothing"); + } else { + createInnerTypeFieldNameConstants(typeNode, errorNode.get(), level, qualified, asEnum, innerTypeName); } } - private void generateFieldNameConstantsForField(EclipseNode fieldNode, ASTNode pos, AccessLevel level, String prefix, String suffix) { - if (hasAnnotation(FieldNameConstants.class, fieldNode)) return; - createFieldNameConstantsForField(level, prefix, suffix, fieldNode, fieldNode, pos, false); - } - - private boolean fieldQualifiesForFieldNameConstantsGeneration(EclipseNode field) { + private boolean fieldQualifiesForFieldNameConstantsGeneration(EclipseNode field, boolean onlyExplicit) { if (field.getKind() != Kind.FIELD) return false; + if (hasAnnotation(FieldNameConstants.Exclude.class, field)) return false; + if (hasAnnotation(FieldNameConstants.Include.class, field)) return true; + if (onlyExplicit) return false; + FieldDeclaration fieldDecl = (FieldDeclaration) field.get(); return filterField(fieldDecl); } @@ -81,55 +93,75 @@ public class HandleFieldNameConstants extends EclipseAnnotationHandler<FieldName handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.FIELD_NAME_CONSTANTS_FLAG_USAGE, "@FieldNameConstants"); EclipseNode node = annotationNode.up(); - FieldNameConstants annotatationInstance = annotation.getInstance(); - AccessLevel level = annotatationInstance.level(); - String prefix = annotatationInstance.prefix(); - String suffix = annotatationInstance.suffix(); - if (prefix.equals(" CONFIG DEFAULT ")) prefix = annotationNode.getAst().readConfiguration(ConfigurationKeys.FIELD_NAME_CONSTANTS_PREFIX); - if (suffix.equals(" CONFIG DEFAULT ")) suffix = annotationNode.getAst().readConfiguration(ConfigurationKeys.FIELD_NAME_CONSTANTS_SUFFIX); - if (prefix == null) prefix = "FIELD_"; - if (suffix == null) suffix = ""; - if (node == null) return; - - switch (node.getKind()) { - case FIELD: - if (level != AccessLevel.NONE) createFieldNameConstantsForFields(level, prefix, suffix, annotationNode.upFromAnnotationToFields(), annotationNode, annotationNode.get(), true); - break; - case TYPE: - if (level == AccessLevel.NONE) { - annotationNode.addWarning("type-level '@FieldNameConstants' does not work with AccessLevel.NONE."); - return; - } - generateFieldNameConstantsForType(node, annotationNode, level, prefix, suffix); - break; - } - } - - private void createFieldNameConstantsForFields(AccessLevel level, String prefix, String suffix, Collection<EclipseNode> fieldNodes, EclipseNode errorNode, ASTNode source, boolean whineIfExists) { - for (EclipseNode fieldNode : fieldNodes) createFieldNameConstantsForField(level, prefix, suffix, fieldNode, errorNode, source, whineIfExists); - } - - private void createFieldNameConstantsForField(AccessLevel level, String prefix, String suffix, EclipseNode fieldNode, EclipseNode errorNode, ASTNode source, boolean whineIfExists) { - if (fieldNode.getKind() != Kind.FIELD) { - errorNode.addError("@FieldNameConstants is only supported on a class, an enum, or a field"); + FieldNameConstants annotationInstance = annotation.getInstance(); + AccessLevel level = annotationInstance.level(); + boolean asEnum = annotationInstance.asEnum(); + boolean usingLombokv1_18_2 = annotation.isExplicit("prefix") || annotation.isExplicit("suffix") || node.getKind() == Kind.FIELD; + + if (usingLombokv1_18_2) { + annotationNode.addError("@FieldNameConstants has been redesigned in lombok v1.18.4; please upgrade your project dependency on lombok. See https://projectlombok.org/features/experimental/FieldNameConstants for more information."); return; } - FieldDeclaration field = (FieldDeclaration) fieldNode.get(); - String fieldName = new String(field.name); - String constantName = prefix + HandlerUtil.camelCaseToConstant(fieldName) + suffix; - if (constantName.equals(fieldName)) { - fieldNode.addWarning("Not generating constant for this field: The name of the constant would be equal to the name of this field."); + if (level == AccessLevel.NONE) { + annotationNode.addWarning("AccessLevel.NONE is not compatible with @FieldNameConstants. If you don't want the inner type, simply remove FieldNameConstants."); return; } - int pS = source.sourceStart, pE = source.sourceEnd; - long p = (long) pS << 32 | pE; - FieldDeclaration fieldConstant = new FieldDeclaration(constantName.toCharArray(), pS,pE); - fieldConstant.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; - fieldConstant.modifiers = toEclipseModifier(level) | Modifier.STATIC | Modifier.FINAL; - fieldConstant.type = new QualifiedTypeReference(TypeConstants.JAVA_LANG_STRING, new long[] {p,p,p}); - fieldConstant.initialization = new StringLiteral(field.name, pS, pE, 0); - injectField(fieldNode.up(), fieldConstant); + String innerTypeName = annotationInstance.innerTypeName(); + if (innerTypeName.isEmpty()) innerTypeName = annotationNode.getAst().readConfiguration(ConfigurationKeys.FIELD_NAME_CONSTANTS_INNER_TYPE_NAME); + if (innerTypeName == null || innerTypeName.isEmpty()) innerTypeName = "Fields"; + + generateFieldNameConstantsForType(node, annotationNode, level, asEnum, innerTypeName, annotationInstance.onlyExplicitlyIncluded()); + } + + private void createInnerTypeFieldNameConstants(EclipseNode typeNode, ASTNode source, AccessLevel level, List<EclipseNode> fields, boolean asEnum, String innerTypeName) { + if (fields.isEmpty()) return; + + TypeDeclaration parent = (TypeDeclaration) typeNode.get(); + TypeDeclaration innerType = new TypeDeclaration(parent.compilationResult); + innerType.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + innerType.modifiers = toEclipseModifier(level) | (asEnum ? ClassFileConstants.AccEnum : ClassFileConstants.AccStatic | ClassFileConstants.AccFinal); + char[] name = innerTypeName.toCharArray(); + innerType.name = name; + innerType.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); + EclipseNode innerNode = injectType(typeNode, innerType); + + ConstructorDeclaration constructor = new ConstructorDeclaration(parent.compilationResult); + constructor.selector = name; + constructor.declarationSourceStart = constructor.sourceStart = source.sourceStart; + constructor.declarationSourceEnd = constructor.sourceEnd = source.sourceEnd; + constructor.modifiers = ClassFileConstants.AccPrivate; + ExplicitConstructorCall superCall = new ExplicitConstructorCall(0); + superCall.sourceStart = source.sourceStart; + superCall.sourceEnd = source.sourceEnd; + superCall.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + constructor.constructorCall = superCall; + if (!asEnum) constructor.statements = new Statement[0]; + + injectMethod(innerNode, constructor); + + if (asEnum) injectMethod(innerNode, new Clinit(parent.compilationResult)); + + for (EclipseNode fieldNode : fields) { + FieldDeclaration field = (FieldDeclaration) fieldNode.get(); + char[] fName = field.name; + int pS = source.sourceStart, pE = source.sourceEnd; + long p = (long) pS << 32 | pE; + FieldDeclaration fieldConstant = new FieldDeclaration(fName, pS, pE); + fieldConstant.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + fieldConstant.modifiers = asEnum ? 0 : ClassFileConstants.AccPublic | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal; + fieldConstant.type = asEnum ? null : new QualifiedTypeReference(TypeConstants.JAVA_LANG_STRING, new long[] {p, p, p}); + if (asEnum) { + AllocationExpression ac = new AllocationExpression(); + ac.enumConstant = fieldConstant; + ac.sourceStart = source.sourceStart; + ac.sourceEnd = source.sourceEnd; + fieldConstant.initialization = ac; + } else { + fieldConstant.initialization = new StringLiteral(field.name, pS, pE, 0); + } + injectField(innerNode, fieldConstant); + } } } diff --git a/src/core/lombok/experimental/FieldNameConstants.java b/src/core/lombok/experimental/FieldNameConstants.java index 31c2970c..d6886890 100644 --- a/src/core/lombok/experimental/FieldNameConstants.java +++ b/src/core/lombok/experimental/FieldNameConstants.java @@ -29,12 +29,32 @@ import java.lang.annotation.Target; import lombok.AccessLevel; /** - * Generates String constants containing the field name for each field. + * Generates an inner type, containing String constants containing the field name for each field. Alternatively, generates an inner enum with enum values matching each field name. */ -@Target({ElementType.TYPE, ElementType.FIELD}) +@Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) public @interface FieldNameConstants { lombok.AccessLevel level() default AccessLevel.PUBLIC; - String prefix() default " CONFIG DEFAULT "; - String suffix() default " CONFIG DEFAULT "; + boolean asEnum() default false; + String innerTypeName() default ""; + + /** + * Only include fields and methods explicitly marked with {@code @FieldNameConstants.Include}. + * Normally, all (non-static) fields are included by default. + */ + boolean onlyExplicitlyIncluded() default false; + + /** + * If present, do not include this field in the generated fieldnames inner type. + */ + @Target(ElementType.FIELD) + @Retention(RetentionPolicy.SOURCE) + public @interface Exclude {} + + /** + * If present, include this field in the generated fieldnames inner type (default). + */ + @Target(ElementType.FIELD) + @Retention(RetentionPolicy.SOURCE) + public @interface Include {} } diff --git a/src/core/lombok/javac/JavacAST.java b/src/core/lombok/javac/JavacAST.java index 4ca2c050..091612cc 100644 --- a/src/core/lombok/javac/JavacAST.java +++ b/src/core/lombok/javac/JavacAST.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2017 The Project Lombok Authors. + * Copyright (C) 2009-2018 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 @@ -93,8 +93,11 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { } @Override public URI getAbsoluteFileLocation() { + return getAbsoluteFileLocation((JCCompilationUnit) top().get()); + } + + public static URI getAbsoluteFileLocation(JCCompilationUnit cu) { try { - JCCompilationUnit cu = (JCCompilationUnit) top().get(); return cu.sourcefile.toUri(); } catch (Exception e) { return null; diff --git a/src/core/lombok/javac/JavacASTVisitor.java b/src/core/lombok/javac/JavacASTVisitor.java index 565980f9..d4f8f731 100644 --- a/src/core/lombok/javac/JavacASTVisitor.java +++ b/src/core/lombok/javac/JavacASTVisitor.java @@ -149,8 +149,12 @@ public interface JavacASTVisitor { print("</CUD>"); } + private String printFlags(long f) { + return Flags.toString(f); + } + @Override public void visitType(JavacNode node, JCClassDecl type) { - print("<TYPE %s>", type.name); + print("<TYPE %s> %s", type.name, printFlags(type.mods.flags)); indent++; if (printContent) { print("%s", type); @@ -185,7 +189,7 @@ public interface JavacASTVisitor { } @Override public void visitField(JavacNode node, JCVariableDecl field) { - print("<FIELD %s %s>", field.vartype, field.name); + print("<FIELD %s %s> %s", field.vartype, field.name, printFlags(field.mods.flags)); indent++; if (printContent) { if (field.init != null) print("%s", field.init); @@ -210,7 +214,7 @@ public interface JavacASTVisitor { type = "DEFAULTCONSTRUCTOR"; } else type = "CONSTRUCTOR"; } else type = "METHOD"; - print("<%s %s> returns: %s", type, method.name, method.restype); + print("<%s %s> %s returns: %s", type, method.name, printFlags(method.mods.flags), method.restype); indent++; if (printContent) { if (method.body == null) print("(ABSTRACT)"); @@ -230,7 +234,7 @@ public interface JavacASTVisitor { } @Override public void visitMethodArgument(JavacNode node, JCVariableDecl arg, JCMethodDecl method) { - print("<METHODARG %s %s>", arg.vartype, arg.name); + print("<METHODARG %s %s> %s", arg.vartype, arg.name, printFlags(arg.mods.flags)); indent++; } @@ -244,7 +248,7 @@ public interface JavacASTVisitor { } @Override public void visitLocal(JavacNode node, JCVariableDecl local) { - print("<LOCAL %s %s>", local.vartype, local.name); + print("<LOCAL %s %s> %s", local.vartype, local.name, printFlags(local.mods.flags)); indent++; } diff --git a/src/core/lombok/javac/JavacNode.java b/src/core/lombok/javac/JavacNode.java index 2bce6e3a..3963c892 100644 --- a/src/core/lombok/javac/JavacNode.java +++ b/src/core/lombok/javac/JavacNode.java @@ -50,11 +50,18 @@ import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; * Javac specific version of the LombokNode class. */ public class JavacNode extends lombok.core.LombokNode<JavacAST, JavacNode, JCTree> { + private JavacAST ast; /** * Passes through to the parent constructor. */ public JavacNode(JavacAST ast, JCTree node, List<JavacNode> children, Kind kind) { - super(ast, node, children, kind); + super(node, children, kind); + this.ast = ast; + } + + @Override + public JavacAST getAst() { + return ast; } public Element getElement() { diff --git a/src/core/lombok/javac/JavacTransformer.java b/src/core/lombok/javac/JavacTransformer.java index 54977a59..2e37b32b 100644 --- a/src/core/lombok/javac/JavacTransformer.java +++ b/src/core/lombok/javac/JavacTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2017 The Project Lombok Authors. + * Copyright (C) 2009-2018 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 @@ -35,6 +35,9 @@ import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.List; +import lombok.ConfigurationKeys; +import lombok.core.LombokConfiguration; + public class JavacTransformer { private final HandlerLibrary handlers; private final Messager messager; @@ -65,7 +68,11 @@ public class JavacTransformer { java.util.List<JavacAST> asts = new ArrayList<JavacAST>(); - for (JCCompilationUnit unit : compilationUnits) asts.add(new JavacAST(messager, context, unit)); + for (JCCompilationUnit unit : compilationUnits) { + if (!Boolean.TRUE.equals(LombokConfiguration.read(ConfigurationKeys.LOMBOK_DISABLE, JavacAST.getAbsoluteFileLocation(unit)))) { + asts.add(new JavacAST(messager, context, unit)); + } + } for (JavacAST ast : asts) { ast.traverse(new AnnotationVisitor(priority)); diff --git a/src/core/lombok/javac/apt/LombokProcessor.java b/src/core/lombok/javac/apt/LombokProcessor.java index 0e6880e4..247d0560 100644 --- a/src/core/lombok/javac/apt/LombokProcessor.java +++ b/src/core/lombok/javac/apt/LombokProcessor.java @@ -43,6 +43,7 @@ import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; +import javax.lang.model.element.QualifiedNameable; import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic.Kind; import javax.tools.JavaFileManager; @@ -166,13 +167,13 @@ public class LombokProcessor extends AbstractProcessor { if (!(originalFiler instanceof InterceptingJavaFileManager)) { final Messager messager = processingEnv.getMessager(); DiagnosticsReceiver receiver = new MessagerDiagnosticsReceiver(messager); - + JavaFileManager newFilerManager = new InterceptingJavaFileManager(originalFiler, receiver); ht.put(key, newFilerManager); Field filerFileManagerField = JavacFiler.class.getDeclaredField("fileManager"); filerFileManagerField.setAccessible(true); filerFileManagerField.set(javacFiler, newFilerManager); - + if (lombok.javac.Javac.getJavaCompilerVersion() > 8 && !lombok.javac.handlers.JavacHandlerUtil.inNetbeansCompileOnSave(context)) { replaceFileManagerJdk9(context, newFilerManager); @@ -383,10 +384,7 @@ public class LombokProcessor extends AbstractProcessor { private String getModuleNameFor(Element element) { while (element != null) { - if (element.getKind().name().equals("MODULE")) { - String n = element.getSimpleName().toString().trim(); - return n.isEmpty() ? null : n; - } + if (element.getKind().name().equals("MODULE")) return ModuleNameOracle.getModuleName(element); Element n = element.getEnclosingElement(); if (n == element) return null; element = n; @@ -394,6 +392,15 @@ public class LombokProcessor extends AbstractProcessor { return null; } + // QualifiedNameable is a java7 thing, so to remain compatible with java6, shove this into an inner class to avoid the ClassNotFoundError. + private static class ModuleNameOracle { + static String getModuleName(Element element) { + if (!(element instanceof QualifiedNameable)) return null; + String name = ((QualifiedNameable) element).getQualifiedName().toString().trim(); + return name.isEmpty() ? null : name; + } + } + private JCCompilationUnit toUnit(Element element) { TreePath path = trees == null ? null : trees.getPath(element); if (path == null) return null; @@ -432,7 +439,7 @@ public class LombokProcessor extends AbstractProcessor { } /** - * This class returns the given filer as a JavacFiler. In case the case that the filer is no + * This class returns the given filer as a JavacFiler. In case the filer is no * JavacFiler (e.g. the Gradle IncrementalFiler), its "delegate" field is used to get the JavacFiler * (directly or through a delegate field again) */ diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index bb495fbc..edf6f2ae 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -99,6 +99,8 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { } @Override public void handle(AnnotationValues<Builder> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder"); + Builder builderInstance = annotation.getInstance(); // These exist just to support the 'old' lombok.experimental.Builder, which had these properties. lombok.Builder no longer has them. @@ -490,18 +492,34 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { JCExpression invoke = call; for (BuilderFieldData bfd : builderFields) { Name setterName = fluent ? bfd.name : type.toName(HandlerUtil.buildAccessorName("set", bfd.name.toString())); - JCExpression arg; + JCExpression[] tgt = new JCExpression[bfd.singularData == null ? 1 : 2]; if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) { - arg = maker.Select(maker.Ident(type.toName("this")), bfd.obtainVia == null ? bfd.rawName : type.toName(bfd.obtainVia.field())); + for (int i = 0; i < tgt.length; i++) { + tgt[i] = maker.Select(maker.Ident(type.toName("this")), bfd.obtainVia == null ? bfd.rawName : type.toName(bfd.obtainVia.field())); + } } else { if (bfd.obtainVia.isStatic()) { - JCExpression c = maker.Select(maker.Ident(type.toName(type.getName())), type.toName(bfd.obtainVia.method())); - arg = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>of(maker.Ident(type.toName("this")))); + for (int i = 0; i < tgt.length; i++) { + JCExpression c = maker.Select(maker.Ident(type.toName(type.getName())), type.toName(bfd.obtainVia.method())); + tgt[i] = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>of(maker.Ident(type.toName("this")))); + } } else { - JCExpression c = maker.Select(maker.Ident(type.toName("this")), type.toName(bfd.obtainVia.method())); - arg = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>nil()); + for (int i = 0; i < tgt.length; i++) { + JCExpression c = maker.Select(maker.Ident(type.toName("this")), type.toName(bfd.obtainVia.method())); + tgt[i] = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>nil()); + } } } + + JCExpression arg; + if (bfd.singularData == null) { + arg = tgt[0]; + } else { + JCExpression eqNull = maker.Binary(CTC_EQUAL, tgt[0], maker.Literal(CTC_BOT, null)); + JCExpression emptyList = maker.Apply(List.<JCExpression>nil(), chainDots(type, "java", "util", "Collections", "emptyList"), List.<JCExpression>nil()); + arg = maker.Conditional(eqNull, emptyList, tgt[1]); + } + invoke = maker.Apply(List.<JCExpression>nil(), maker.Select(invoke, setterName), List.of(arg)); } JCStatement statement = maker.Return(invoke); diff --git a/src/core/lombok/javac/handlers/HandleFieldDefaults.java b/src/core/lombok/javac/handlers/HandleFieldDefaults.java index 52f6c39c..aa381c0d 100644 --- a/src/core/lombok/javac/handlers/HandleFieldDefaults.java +++ b/src/core/lombok/javac/handlers/HandleFieldDefaults.java @@ -44,7 +44,7 @@ import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; /** - * Handles the {@code lombok.FieldDefaults} annotation for eclipse. + * Handles the {@code lombok.FieldDefaults} annotation for javac. */ @ProviderFor(JavacASTVisitor.class) @HandlerPriority(-2048) //-2^11; to ensure @Value picks up on messing with the fields' 'final' state, run earlier. diff --git a/src/core/lombok/javac/handlers/HandleFieldNameConstants.java b/src/core/lombok/javac/handlers/HandleFieldNameConstants.java index 8ff136fc..5b120948 100644 --- a/src/core/lombok/javac/handlers/HandleFieldNameConstants.java +++ b/src/core/lombok/javac/handlers/HandleFieldNameConstants.java @@ -24,13 +24,11 @@ package lombok.javac.handlers; import static lombok.core.handlers.HandlerUtil.handleExperimentalFlagUsage; import static lombok.javac.handlers.JavacHandlerUtil.*; -import java.lang.reflect.Modifier; -import java.util.Collection; +import java.util.ArrayList; import lombok.AccessLevel; import lombok.ConfigurationKeys; import lombok.core.AST.Kind; -import lombok.core.handlers.HandlerUtil; import lombok.core.AnnotationValues; import lombok.experimental.FieldNameConstants; import lombok.javac.JavacAnnotationHandler; @@ -40,16 +38,23 @@ import lombok.javac.JavacTreeMaker; 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.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCModifiers; +import com.sun.tools.javac.tree.JCTree.JCStatement; +import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.Name; @ProviderFor(JavacAnnotationHandler.class) public class HandleFieldNameConstants extends JavacAnnotationHandler<FieldNameConstants> { - public void generateFieldNameConstantsForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, String prefix, String suffix) { + public void generateFieldNameConstantsForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean asEnum, String innerTypeName, boolean onlyExplicit) { JCClassDecl typeDecl = null; if (typeNode.get() instanceof JCClassDecl) typeDecl = (JCClassDecl) typeNode.get(); @@ -57,22 +62,31 @@ public class HandleFieldNameConstants extends JavacAnnotationHandler<FieldNameCo boolean notAClass = (modifiers & (Flags.INTERFACE | Flags.ANNOTATION)) != 0; if (typeDecl == null || notAClass) { - errorNode.addError("@FieldNameConstants is only supported on a class, an enum, or a field."); + errorNode.addError("@FieldNameConstants is only supported on a class or an enum."); return; } + java.util.List<JavacNode> qualified = new ArrayList<JavacNode>(); + for (JavacNode field : typeNode.down()) { - if (fieldQualifiesForFieldNameConstantsGeneration(field)) generateFieldNameConstantsForField(field, errorNode.get(), level, prefix, suffix); + if (fieldQualifiesForFieldNameConstantsGeneration(field, onlyExplicit)) qualified.add(field); + } + + if (qualified.isEmpty()) { + errorNode.addWarning("No fields qualify for @FieldNameConstants, therefore this annotation does nothing"); + } else { + createInnerTypeFieldNameConstants(typeNode, errorNode.get(), level, qualified, asEnum, innerTypeName); } } - private void generateFieldNameConstantsForField(JavacNode fieldNode, DiagnosticPosition pos, AccessLevel level, String prefix, String suffix) { - if (hasAnnotation(FieldNameConstants.class, fieldNode)) return; - createFieldNameConstantsForField(level, prefix, suffix, fieldNode, fieldNode, false); - } - - private boolean fieldQualifiesForFieldNameConstantsGeneration(JavacNode field) { + private boolean fieldQualifiesForFieldNameConstantsGeneration(JavacNode field, boolean onlyExplicit) { if (field.getKind() != Kind.FIELD) return false; + boolean exclAnn = JavacHandlerUtil.hasAnnotationAndDeleteIfNeccessary(FieldNameConstants.Exclude.class, field); + boolean inclAnn = JavacHandlerUtil.hasAnnotationAndDeleteIfNeccessary(FieldNameConstants.Include.class, field); + if (exclAnn) return false; + if (inclAnn) return true; + if (onlyExplicit) return false; + JCVariableDecl fieldDecl = (JCVariableDecl) field.get(); if (fieldDecl.name.toString().startsWith("$")) return false; if ((fieldDecl.mods.flags & Flags.STATIC) != 0) return false; @@ -82,56 +96,61 @@ public class HandleFieldNameConstants extends JavacAnnotationHandler<FieldNameCo public void handle(AnnotationValues<FieldNameConstants> annotation, JCAnnotation ast, JavacNode annotationNode) { handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.FIELD_NAME_CONSTANTS_FLAG_USAGE, "@FieldNameConstants"); - Collection<JavacNode> fields = annotationNode.upFromAnnotationToFields(); deleteAnnotationIfNeccessary(annotationNode, FieldNameConstants.class); deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); JavacNode node = annotationNode.up(); - FieldNameConstants annotatationInstance = annotation.getInstance(); - AccessLevel level = annotatationInstance.level(); - String prefix = annotatationInstance.prefix(); - String suffix = annotatationInstance.suffix(); - if (prefix.equals(" CONFIG DEFAULT ")) prefix = annotationNode.getAst().readConfiguration(ConfigurationKeys.FIELD_NAME_CONSTANTS_PREFIX); - if (suffix.equals(" CONFIG DEFAULT ")) suffix = annotationNode.getAst().readConfiguration(ConfigurationKeys.FIELD_NAME_CONSTANTS_SUFFIX); - if (prefix == null) prefix = "FIELD_"; - if (suffix == null) suffix = ""; - if (node == null) return; - switch (node.getKind()) { - case FIELD: - if (level != AccessLevel.NONE) createFieldNameConstantsForFields(level, prefix, suffix, fields, annotationNode, annotationNode, true); - break; - case TYPE: - if (level == AccessLevel.NONE) { - annotationNode.addWarning("type-level '@FieldNameConstants' does not work with AccessLevel.NONE."); - return; - } - generateFieldNameConstantsForType(node, annotationNode, level, prefix, suffix); - break; - } - } - - private void createFieldNameConstantsForFields(AccessLevel level, String prefix, String suffix, Collection<JavacNode> fieldNodes, JavacNode annotationNode, JavacNode errorNode, boolean whineIfExists) { - for (JavacNode fieldNode : fieldNodes) createFieldNameConstantsForField(level, prefix, suffix, fieldNode, errorNode, whineIfExists); - } - - private void createFieldNameConstantsForField(AccessLevel level, String prefix, String suffix, JavacNode fieldNode, JavacNode source, boolean whineIfExists) { - if (fieldNode.getKind() != Kind.FIELD) { - source.addError("@FieldNameConstants is only supported on a class, an enum, or a field"); + FieldNameConstants annotationInstance = annotation.getInstance(); + AccessLevel level = annotationInstance.level(); + boolean asEnum = annotationInstance.asEnum(); + boolean usingLombokv1_18_2 = annotation.isExplicit("prefix") || annotation.isExplicit("suffix") || node.getKind() == Kind.FIELD; + + if (usingLombokv1_18_2) { + annotationNode.addError("@FieldNameConstants has been redesigned in lombok v1.18.4; please upgrade your project dependency on lombok. See https://projectlombok.org/features/experimental/FieldNameConstants for more information."); return; } - JCVariableDecl field = (JCVariableDecl) fieldNode.get(); - String fieldName = field.name.toString(); - String constantName = prefix + HandlerUtil.camelCaseToConstant(fieldName) + suffix; - if (constantName.equals(fieldName)) { - fieldNode.addWarning("Not generating constant for this field: The name of the constant would be equal to the name of this field."); + + if (level == AccessLevel.NONE) { + annotationNode.addWarning("AccessLevel.NONE is not compatible with @FieldNameConstants. If you don't want the inner type, simply remove @FieldNameConstants."); return; } - JavacTreeMaker treeMaker = fieldNode.getTreeMaker(); - JCModifiers modifiers = treeMaker.Modifiers(toJavacModifier(level) | Modifier.STATIC | Modifier.FINAL); - JCExpression returnType = chainDots(fieldNode, "java", "lang", "String"); - JCExpression init = treeMaker.Literal(fieldNode.getName()); - JCVariableDecl fieldConstant = treeMaker.VarDef(modifiers, fieldNode.toName(constantName), returnType, init); - injectField(fieldNode.up(), fieldConstant); + String innerTypeName = annotationInstance.innerTypeName(); + if (innerTypeName.isEmpty()) innerTypeName = annotationNode.getAst().readConfiguration(ConfigurationKeys.FIELD_NAME_CONSTANTS_INNER_TYPE_NAME); + if (innerTypeName == null || innerTypeName.isEmpty()) innerTypeName = "Fields"; + + generateFieldNameConstantsForType(node, annotationNode, level, asEnum, innerTypeName, annotationInstance.onlyExplicitlyIncluded()); + } + + private void createInnerTypeFieldNameConstants(JavacNode typeNode, DiagnosticPosition pos, AccessLevel level, java.util.List<JavacNode> fields, boolean asEnum, String innerTypeName) { + if (fields.isEmpty()) return; + + JavacTreeMaker maker = typeNode.getTreeMaker(); + JCModifiers mods = maker.Modifiers(toJavacModifier(level) | (asEnum ? Flags.ENUM : Flags.STATIC | Flags.FINAL)); + + Name fieldsName = typeNode.toName(innerTypeName); + JCClassDecl innerType = maker.ClassDef(mods, fieldsName, List.<JCTypeParameter>nil(), null, List.<JCExpression>nil(), List.<JCTree>nil()); + JavacNode innerNode = injectType(typeNode, innerType); + + JCModifiers genConstrMods = maker.Modifiers(Flags.GENERATEDCONSTR | (asEnum ? 0L : Flags.PRIVATE)); + JCBlock genConstrBody = maker.Block(0L, List.<JCStatement>of(maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(typeNode.toName("super")), List.<JCExpression>nil())))); + JCMethodDecl genConstr = maker.MethodDef(genConstrMods, typeNode.toName("<init>"), null, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), genConstrBody, null); + + injectMethod(innerNode, genConstr); + + for (JavacNode field : fields) { + JCModifiers enumValueMods = maker.Modifiers(Flags.PUBLIC | Flags.STATIC | Flags.FINAL | (asEnum ? Flags.ENUM : 0L)); + JCExpression returnType; + JCExpression init; + if (asEnum) { + returnType = maker.Ident(fieldsName); + init = maker.NewClass(null, List.<JCExpression>nil(), maker.Ident(fieldsName), List.<JCExpression>nil(), null); + } else { + returnType = chainDots(field, "java", "lang", "String"); + init = maker.Literal(field.getName()); + } + JCVariableDecl enumField = maker.VarDef(enumValueMods, ((JCVariableDecl) field.get()).name, returnType, init); + injectField(innerNode, enumField); + } } -}
\ No newline at end of file +} diff --git a/src/core9/module-info.java b/src/core9/module-info.java index f4d5815f..56e54270 100644 --- a/src/core9/module-info.java +++ b/src/core9/module-info.java @@ -23,6 +23,7 @@ module lombok { requires java.compiler; requires java.instrument; requires jdk.unsupported; + requires static org.mapstruct.processor; exports lombok; exports lombok.experimental; @@ -36,4 +37,3 @@ module lombok { provides javax.annotation.processing.Processor with lombok.launch.AnnotationProcessorHider.AnnotationProcessor; provides org.mapstruct.ap.spi.AstModifyingAnnotationProcessor with lombok.launch.AnnotationProcessorHider.AstModificationNotifier; } - diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java b/src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java index 52f63765..c0a2c6fe 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java @@ -32,6 +32,7 @@ import lombok.core.Version; public class PatchFixesShadowLoaded { public static String addLombokNotesToEclipseAboutDialog(String origReturnValue, String key) { if ("aboutText".equals(key)) { + if (origReturnValue.contains(" is installed. https://projectlombok.org")) return origReturnValue; return origReturnValue + "\n\nLombok " + Version.getFullVersion() + " is installed. https://projectlombok.org/"; } return origReturnValue; diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java index c2a362bd..fee104d3 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java @@ -210,16 +210,16 @@ public class PatchVal { if (local == null || !LocalDeclaration.class.equals(local.getClass())) return false; boolean decomponent = false; - boolean val = isVal(local, scope); - boolean var = isVar(local, scope); - if (!(val || var)) return false; + boolean val = isVal(local, scope); + boolean var = isVar(local, scope); + if (!(val || var)) return false; StackTraceElement[] st = new Throwable().getStackTrace(); for (int i = 0; i < st.length - 2 && i < 10; i++) { if (st[i].getClassName().equals("lombok.launch.PatchFixesHider$Val")) { boolean valInForStatement = val && - st[i + 1].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration") && - st[i + 2].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.ForStatement"); + st[i + 1].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration") && + st[i + 2].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.ForStatement"); if (valInForStatement) return false; break; } @@ -269,7 +269,7 @@ public class PatchVal { } } - if(val) local.modifiers |= ClassFileConstants.AccFinal; + if (val) local.modifiers |= ClassFileConstants.AccFinal; local.annotations = addValAnnotation(local.annotations, local.type, scope); local.type = replacement != null ? replacement : new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(local.type, 3)); @@ -298,7 +298,7 @@ public class PatchVal { if (val) forEach.elementVariable.modifiers |= ClassFileConstants.AccFinal; forEach.elementVariable.annotations = addValAnnotation(forEach.elementVariable.annotations, forEach.elementVariable.type, scope); forEach.elementVariable.type = replacement != null ? replacement : - new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(forEach.elementVariable.type, 3)); + new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(forEach.elementVariable.type, 3)); return false; } @@ -326,7 +326,7 @@ public class PatchVal { resolved = ((ArrayBinding) resolved).elementsType(); return resolved; } else if (resolved instanceof ReferenceBinding) { - ReferenceBinding iterableType = ((ReferenceBinding)resolved).findSuperTypeOriginatingFrom(TypeIds.T_JavaLangIterable, false); + ReferenceBinding iterableType = ((ReferenceBinding) resolved).findSuperTypeOriginatingFrom(TypeIds.T_JavaLangIterable, false); TypeBinding[] arguments = null; if (iterableType != null) switch (iterableType.kind()) { diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java index d59b6a2e..99447bae 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java @@ -47,9 +47,9 @@ import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.ForeachStatement; import org.eclipse.jdt.internal.compiler.ast.ImportReference; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; +import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.TypeReference; -import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.parser.Parser; public class PatchValEclipse { @@ -103,6 +103,10 @@ public class PatchValEclipse { } } + private static boolean couldBeVal(ImportReference[] imports, TypeReference type) { + return PatchVal.couldBe(imports, "lombok.val", type); + } + private static boolean couldBeVar(ImportReference[] imports, TypeReference type) { return PatchVal.couldBe(imports, "lombok.experimental.var", type) || PatchVal.couldBe(imports, "lombok.var", type); } @@ -118,17 +122,18 @@ public class PatchValEclipse { } public static void addFinalAndValAnnotationToModifierList(Object converter, List<IExtendedModifier> modifiers, AST ast, LocalDeclaration in) { - // First check that 'in' has the final flag on, and a @val / @lombok.val annotation. - if ((in.modifiers & ClassFileConstants.AccFinal) == 0) return; + // First check that 'in' has the final flag on, and a @val / @lombok.val / @var / @lombok.var annotation. if (in.annotations == null) return; boolean found = false; - Annotation valAnnotation = null; - + Annotation valAnnotation = null, varAnnotation = null; for (Annotation ann : in.annotations) { if (couldBeVal(null, ann.type)) { found = true; valAnnotation = ann; - break; + } + if (couldBeVar(null, ann.type)) { + found = true; + varAnnotation = ann; } } @@ -139,10 +144,11 @@ public class PatchValEclipse { if (modifiers == null) return; // This is null only if the project is 1.4 or less. Lombok doesn't work in that. boolean finalIsPresent = false; boolean valIsPresent = false; + boolean varIsPresent = false; for (Object present : modifiers) { if (present instanceof Modifier) { - ModifierKeyword keyword = ((Modifier)present).getKeyword(); + ModifierKeyword keyword = ((Modifier) present).getKeyword(); if (keyword == null) continue; if (keyword.toFlagValue() == Modifier.FINAL) finalIsPresent = true; } @@ -151,20 +157,18 @@ public class PatchValEclipse { Name typeName = ((org.eclipse.jdt.core.dom.Annotation) present).getTypeName(); if (typeName != null) { String fullyQualifiedName = typeName.getFullyQualifiedName(); - if ("val".equals(fullyQualifiedName) || "lombok.val".equals(fullyQualifiedName)) { - valIsPresent = true; - } + if ("val".equals(fullyQualifiedName) || "lombok.val".equals(fullyQualifiedName)) valIsPresent = true; + if ("var".equals(fullyQualifiedName) || "lombok.var".equals(fullyQualifiedName) || "lombok.experimental.var".equals(fullyQualifiedName)) varIsPresent = true; } } } - if (!finalIsPresent) { - modifiers.add( - createModifier(ast, ModifierKeyword.FINAL_KEYWORD, valAnnotation.sourceStart, valAnnotation.sourceEnd)); + if (!finalIsPresent && valAnnotation != null) { + modifiers.add(createModifier(ast, ModifierKeyword.FINAL_KEYWORD, valAnnotation.sourceStart, valAnnotation.sourceEnd)); } - if (!valIsPresent) { - MarkerAnnotation newAnnotation = createValAnnotation(ast, valAnnotation, valAnnotation.sourceStart, valAnnotation.sourceEnd); + if (!valIsPresent && valAnnotation != null) { + MarkerAnnotation newAnnotation = createValVarAnnotation(ast, valAnnotation, valAnnotation.sourceStart, valAnnotation.sourceEnd); try { Reflection.astConverterRecordNodes.invoke(converter, newAnnotation, valAnnotation); Reflection.astConverterRecordNodes.invoke(converter, newAnnotation.getTypeName(), valAnnotation.type); @@ -175,10 +179,19 @@ public class PatchValEclipse { } modifiers.add(newAnnotation); } - } - - private static boolean couldBeVal(ImportReference[] imports, TypeReference type) { - return PatchVal.couldBe(imports, "lombok.val", type); + + if (!varIsPresent && varAnnotation != null) { + MarkerAnnotation newAnnotation = createValVarAnnotation(ast, varAnnotation, varAnnotation.sourceStart, varAnnotation.sourceEnd); + try { + Reflection.astConverterRecordNodes.invoke(converter, newAnnotation, varAnnotation); + Reflection.astConverterRecordNodes.invoke(converter, newAnnotation.getTypeName(), varAnnotation.type); + } catch (IllegalAccessException e) { + throw Lombok.sneakyThrow(e); + } catch (InvocationTargetException e) { + throw Lombok.sneakyThrow(e.getCause()); + } + modifiers.add(newAnnotation); + } } public static Modifier createModifier(AST ast, ModifierKeyword keyword, int start, int end) { @@ -200,7 +213,7 @@ public class PatchValEclipse { return modifier; } - public static MarkerAnnotation createValAnnotation(AST ast, Annotation original, int start, int end) { + public static MarkerAnnotation createValVarAnnotation(AST ast, Annotation original, int start, int end) { MarkerAnnotation out = null; try { out = Reflection.markerAnnotationConstructor.newInstance(ast); @@ -212,13 +225,23 @@ public class PatchValEclipse { throw Lombok.sneakyThrow(e); } + char[][] tokens; + if (original.type instanceof SingleTypeReference) { + tokens = new char[1][]; + tokens[0] = ((SingleTypeReference) original.type).token; + } else if (original.type instanceof QualifiedTypeReference) { + tokens = ((QualifiedTypeReference) original.type).tokens; + } else { + return null; + } + if (out != null) { - SimpleName valName = ast.newSimpleName("val"); + SimpleName valName = ast.newSimpleName(new String(tokens[tokens.length - 1])); valName.setSourceRange(start, end - start + 1); - if (original.type instanceof SingleTypeReference) { + if (tokens.length == 1) { out.setTypeName(valName); setIndex(valName, 1); - } else { + } else if (tokens.length == 2) { SimpleName lombokName = ast.newSimpleName("lombok"); lombokName.setSourceRange(start, end - start + 1); setIndex(lombokName, 1); @@ -227,6 +250,21 @@ public class PatchValEclipse { setIndex(fullName, 1); fullName.setSourceRange(start, end - start + 1); out.setTypeName(fullName); + } else { + SimpleName lombokName = ast.newSimpleName("lombok"); + lombokName.setSourceRange(start, end - start + 1); + SimpleName experimentalName = ast.newSimpleName("experimental"); + lombokName.setSourceRange(start, end - start + 1); + setIndex(lombokName, 1); + setIndex(experimentalName, 2); + setIndex(valName, 3); + QualifiedName lombokExperimentalName = ast.newQualifiedName(lombokName, experimentalName); + lombokExperimentalName.setSourceRange(start, end - start + 1); + setIndex(lombokExperimentalName, 1); + QualifiedName fullName = ast.newQualifiedName(lombokExperimentalName, valName); + setIndex(fullName, 1); + fullName.setSourceRange(start, end - start + 1); + out.setTypeName(fullName); } out.setSourceRange(start, end - start + 1); } diff --git a/src/eclipseAgent/lombok/launch/PatchFixesHider.java b/src/eclipseAgent/lombok/launch/PatchFixesHider.java index 317b06a4..3c70d81d 100644 --- a/src/eclipseAgent/lombok/launch/PatchFixesHider.java +++ b/src/eclipseAgent/lombok/launch/PatchFixesHider.java @@ -92,7 +92,7 @@ final class PatchFixesHider { shadowLoader = Util.class.getClassLoader(); } catch (ClassNotFoundException e) { // If we get here, it isn't, and we should use the shadowloader. - shadowLoader = Main.createShadowClassLoader(); + shadowLoader = Main.getShadowClassLoader(); } } diff --git a/src/launch/lombok/launch/Agent.java b/src/launch/lombok/launch/Agent.java index 7989e51f..357a8e48 100644 --- a/src/launch/lombok/launch/Agent.java +++ b/src/launch/lombok/launch/Agent.java @@ -35,7 +35,7 @@ final class Agent { } private static void runLauncher(String agentArgs, Instrumentation instrumentation, boolean injected) throws Throwable { - ClassLoader cl = Main.createShadowClassLoader(); + ClassLoader cl = Main.getShadowClassLoader(); try { Class<?> c = cl.loadClass("lombok.core.AgentLauncher"); Method m = c.getDeclaredMethod("runAgents", String.class, Instrumentation.class, boolean.class, Class.class); diff --git a/src/launch/lombok/launch/AnnotationProcessor.java b/src/launch/lombok/launch/AnnotationProcessor.java index c4f922b9..b03dec76 100644 --- a/src/launch/lombok/launch/AnnotationProcessor.java +++ b/src/launch/lombok/launch/AnnotationProcessor.java @@ -104,7 +104,7 @@ class AnnotationProcessorHider { } private static AbstractProcessor createWrappedInstance() { - ClassLoader cl = Main.createShadowClassLoader(); + ClassLoader cl = Main.getShadowClassLoader(); try { Class<?> mc = cl.loadClass("lombok.core.AnnotationProcessor"); return (AbstractProcessor) mc.getDeclaredConstructor().newInstance(); diff --git a/src/launch/lombok/launch/Main.java b/src/launch/lombok/launch/Main.java index b81b6268..82913f8e 100644 --- a/src/launch/lombok/launch/Main.java +++ b/src/launch/lombok/launch/Main.java @@ -25,12 +25,18 @@ import java.lang.reflect.InvocationTargetException; import java.util.Arrays; class Main { - static ClassLoader createShadowClassLoader() { - return new ShadowClassLoader(Main.class.getClassLoader(), "lombok", null, Arrays.<String>asList(), Arrays.asList("lombok.patcher.Symbols")); + + private static ShadowClassLoader classLoader; + + static synchronized ClassLoader getShadowClassLoader() { + if (classLoader == null) { + classLoader = new ShadowClassLoader(Main.class.getClassLoader(), "lombok", null, Arrays.<String>asList(), Arrays.asList("lombok.patcher.Symbols")); + } + return classLoader; } public static void main(String[] args) throws Throwable { - ClassLoader cl = createShadowClassLoader(); + ClassLoader cl = getShadowClassLoader(); Class<?> mc = cl.loadClass("lombok.core.Main"); try { mc.getMethod("main", String[].class).invoke(null, new Object[] {args}); diff --git a/src/launch/lombok/launch/ShadowClassLoader.java b/src/launch/lombok/launch/ShadowClassLoader.java index 36dd289c..5489f3ea 100644 --- a/src/launch/lombok/launch/ShadowClassLoader.java +++ b/src/launch/lombok/launch/ShadowClassLoader.java @@ -296,17 +296,18 @@ class ShadowClassLoader extends ClassLoader { return null; } - private boolean partOfShadow(URL item, String name) { - return inOwnBase(item, name) || isPartOfShadowSuffix(item, name, sclSuffix); + private boolean partOfShadow(String item, String name) { + return !name.startsWith("java/") + && !name.startsWith("sun/") + && (inOwnBase(item, name) || isPartOfShadowSuffix(item, name, sclSuffix)); } /** * Checks if the stated item is located inside the same classpath root as the jar that hosts ShadowClassLoader.class. {@code item} and {@code name} refer to the same thing. */ - private boolean inOwnBase(URL item, String name) { + private boolean inOwnBase(String item, String name) { if (item == null) return false; - String itemString = item.toString(); - return (itemString.length() == SELF_BASE_LENGTH + name.length()) && SELF_BASE.regionMatches(0, itemString, 0, SELF_BASE_LENGTH); + return (item.length() == SELF_BASE_LENGTH + name.length()) && SELF_BASE.regionMatches(0, item, 0, SELF_BASE_LENGTH); } private static boolean sclFileContainsSuffix(InputStream in, String suffix) throws IOException { @@ -386,12 +387,11 @@ class ShadowClassLoader extends ClassLoader { } } - private boolean isPartOfShadowSuffix(URL item, String name, String suffix) { + private boolean isPartOfShadowSuffix(String url, String name, String suffix) { // Instead of throwing an exception or logging, weird, unexpected cases just return false. // This is better than throwing an exception, because exceptions would make your build tools unusable. // Such cases are marked with the comment: // *unexpected* - if (item == null) return false; - String url = item.toString(); + if (url == null) return false; if (url.startsWith("file:/")) { url = urlDecode(url.substring(5)); if (url.length() <= name.length() || !url.endsWith(name) || url.charAt(url.length() - name.length() - 1) != '/') { @@ -436,14 +436,14 @@ class ShadowClassLoader extends ClassLoader { Enumeration<URL> sec = super.getResources(name); while (sec.hasMoreElements()) { URL item = sec.nextElement(); - if (!partOfShadow(item, name)) vector.add(item); + if (!partOfShadow(item.toString(), name)) vector.add(item); } if (altName != null) { Enumeration<URL> tern = super.getResources(altName); while (tern.hasMoreElements()) { URL item = tern.nextElement(); - if (!partOfShadow(item, altName)) vector.add(item); + if (!partOfShadow(item.toString(), altName)) vector.add(item); } } @@ -483,11 +483,11 @@ class ShadowClassLoader extends ClassLoader { if (altName != null) { URL res = super.getResource(altName); - if (res != null && (!noSuper || partOfShadow(res, altName))) return res; + if (res != null && (!noSuper || partOfShadow(res.toString(), altName))) return res; } URL res = super.getResource(name); - if (res != null && (!noSuper || partOfShadow(res, name))) return res; + if (res != null && (!noSuper || partOfShadow(res.toString(), name))) return res; return null; } @@ -501,12 +501,12 @@ class ShadowClassLoader extends ClassLoader { private URL getResourceSkippingSelf(String name) throws IOException { URL candidate = super.getResource(name); if (candidate == null) return null; - if (!partOfShadow(candidate, name)) return candidate; + if (!partOfShadow(candidate.toString(), name)) return candidate; Enumeration<URL> en = super.getResources(name); while (en.hasMoreElements()) { candidate = en.nextElement(); - if (!partOfShadow(candidate, name)) return candidate; + if (!partOfShadow(candidate.toString(), name)) return candidate; } return null; @@ -526,9 +526,14 @@ class ShadowClassLoader extends ClassLoader { String fileNameOfClass = name.replace(".", "/") + ".class"; URL res = getResource_(fileNameOfClass, true); if (res == null) { - if (!exclusionListMatch(fileNameOfClass)) return super.loadClass(name, resolve); - throw new ClassNotFoundException(name); + if (!exclusionListMatch(fileNameOfClass)) try { + return super.loadClass(name, resolve); + } catch (ClassNotFoundException cnfe) { + res = getResource_("secondaryLoading.SCL." + sclSuffix + "/" + name.replace(".", "/") + ".SCL." + sclSuffix, true); + if (res == null) throw cnfe; + } } + if (res == null) throw new ClassNotFoundException(name); byte[] b; int p = 0; diff --git a/src/utils/lombok/eclipse/Eclipse.java b/src/utils/lombok/eclipse/Eclipse.java index 5ef33086..943a7a7a 100644 --- a/src/utils/lombok/eclipse/Eclipse.java +++ b/src/utils/lombok/eclipse/Eclipse.java @@ -56,6 +56,8 @@ public class Eclipse { */ public static final int ECLIPSE_DO_NOT_TOUCH_FLAG = ASTNode.Bit24; + private static final Pattern SPLIT_AT_DOT = Pattern.compile("\\."); + private Eclipse() { //Prevent instantiation } @@ -65,19 +67,25 @@ 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) { - int len = typeName.length - 1; + int len = typeName.length - 1; // number of dots + if (len == 0) return new String(typeName[0]); + for (char[] c : typeName) len += c.length; - StringBuilder sb = new StringBuilder(len); - boolean first = true; - for (char[] c : typeName) { - sb.append(first ? "" : ".").append(c); - first = false; + char[] ret = new char[len]; + char[] part = typeName[0]; + System.arraycopy(part, 0, ret, 0, part.length); + int pos = part.length; + for (int i = 1; i < typeName.length; i++) { + ret[pos++] = '.'; + part = typeName[i]; + System.arraycopy(part, 0, ret, pos, part.length); + pos += part.length; } - return sb.toString(); + return new String(ret); } public static char[][] fromQualifiedName(String typeName) { - String[] split = typeName.split("\\."); + String[] split = SPLIT_AT_DOT.split(typeName); char[][] result = new char[split.length][]; for (int i = 0; i < split.length; i++) { result[i] = split[i].toCharArray(); diff --git a/test/core/src/lombok/AbstractRunTests.java b/test/core/src/lombok/AbstractRunTests.java index 3d672bc4..f93fbe27 100644 --- a/test/core/src/lombok/AbstractRunTests.java +++ b/test/core/src/lombok/AbstractRunTests.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 The Project Lombok Authors. + * Copyright (C) 2009-2018 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,6 +30,7 @@ import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.StringWriter; +import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; @@ -40,7 +41,6 @@ import java.util.Map; import org.junit.Assert; import lombok.DirectoryRunner.FileTester; -import lombok.core.AST; import lombok.core.LombokConfiguration; import lombok.core.LombokImmutableList; import lombok.core.configuration.ConfigurationKeysLoader; @@ -84,7 +84,7 @@ public abstract class AbstractRunTests { StringWriter writer = new StringWriter(); LombokConfiguration.overrideConfigurationResolverFactory(new ConfigurationResolverFactory() { - @Override public ConfigurationResolver createResolver(AST<?, ?, ?> ast) { + @Override public ConfigurationResolver createResolver(URI sourceLocation) { return sourceDirectives_.getConfiguration(); } }); diff --git a/test/core/src/lombok/RunTestsViaEcj.java b/test/core/src/lombok/RunTestsViaEcj.java index 4e6a6a55..3efe38f5 100644 --- a/test/core/src/lombok/RunTestsViaEcj.java +++ b/test/core/src/lombok/RunTestsViaEcj.java @@ -122,7 +122,7 @@ public class RunTestsViaEcj extends AbstractRunTests { CompilationUnitDeclaration cud = compilationUnit_.get(); - if (cud == null) result.append("---- NO CompilationUnit provided by ecj ----"); + if (cud == null) result.append("---- No CompilationUnit provided by ecj ----"); else result.append(cud.toString()); return true; diff --git a/test/transform/resource/after-delombok/BuilderSingularToBuilderWithNull.java b/test/transform/resource/after-delombok/BuilderSingularToBuilderWithNull.java new file mode 100644 index 00000000..1f472438 --- /dev/null +++ b/test/transform/resource/after-delombok/BuilderSingularToBuilderWithNull.java @@ -0,0 +1,63 @@ +class BuilderSingularToBuilderWithNull { + private java.util.List<String> elems; + public static void test() { + new BuilderSingularToBuilderWithNull(null).toBuilder(); + } + @java.lang.SuppressWarnings("all") + BuilderSingularToBuilderWithNull(final java.util.List<String> elems) { + this.elems = elems; + } + @java.lang.SuppressWarnings("all") + public static class BuilderSingularToBuilderWithNullBuilder { + @java.lang.SuppressWarnings("all") + private java.util.ArrayList<String> elems; + @java.lang.SuppressWarnings("all") + BuilderSingularToBuilderWithNullBuilder() { + } + @java.lang.SuppressWarnings("all") + public BuilderSingularToBuilderWithNullBuilder elem(final String elem) { + if (this.elems == null) this.elems = new java.util.ArrayList<String>(); + this.elems.add(elem); + return this; + } + @java.lang.SuppressWarnings("all") + public BuilderSingularToBuilderWithNullBuilder elems(final java.util.Collection<? extends String> elems) { + if (this.elems == null) this.elems = new java.util.ArrayList<String>(); + this.elems.addAll(elems); + return this; + } + @java.lang.SuppressWarnings("all") + public BuilderSingularToBuilderWithNullBuilder clearElems() { + if (this.elems != null) this.elems.clear(); + return this; + } + @java.lang.SuppressWarnings("all") + public BuilderSingularToBuilderWithNull build() { + java.util.List<String> elems; + switch (this.elems == null ? 0 : this.elems.size()) { + case 0: + elems = java.util.Collections.emptyList(); + break; + case 1: + elems = java.util.Collections.singletonList(this.elems.get(0)); + break; + default: + elems = java.util.Collections.unmodifiableList(new java.util.ArrayList<String>(this.elems)); + } + return new BuilderSingularToBuilderWithNull(elems); + } + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "BuilderSingularToBuilderWithNull.BuilderSingularToBuilderWithNullBuilder(elems=" + this.elems + ")"; + } + } + @java.lang.SuppressWarnings("all") + public static BuilderSingularToBuilderWithNullBuilder builder() { + return new BuilderSingularToBuilderWithNullBuilder(); + } + @java.lang.SuppressWarnings("all") + public BuilderSingularToBuilderWithNullBuilder toBuilder() { + return new BuilderSingularToBuilderWithNullBuilder().elems(this.elems == null ? java.util.Collections.emptyList() : this.elems); + } +} diff --git a/test/transform/resource/after-delombok/BuilderWithToBuilder.java b/test/transform/resource/after-delombok/BuilderWithToBuilder.java index 46387f0f..b644a16f 100644 --- a/test/transform/resource/after-delombok/BuilderWithToBuilder.java +++ b/test/transform/resource/after-delombok/BuilderWithToBuilder.java @@ -86,7 +86,7 @@ class BuilderWithToBuilder<T> { } @java.lang.SuppressWarnings("all") public BuilderWithToBuilderBuilder<T> toBuilder() { - return new BuilderWithToBuilderBuilder<T>().one(this.mOne).two(this.mTwo).foo(BuilderWithToBuilder.rrr(this)).bars(this.bars); + return new BuilderWithToBuilderBuilder<T>().one(this.mOne).two(this.mTwo).foo(BuilderWithToBuilder.rrr(this)).bars(this.bars == null ? java.util.Collections.emptyList() : this.bars); } } class ConstructorWithToBuilder<T> { diff --git a/test/transform/resource/after-delombok/FieldNameConstantsBasic.java b/test/transform/resource/after-delombok/FieldNameConstantsBasic.java index 4e547aa5..8be45d2b 100644 --- a/test/transform/resource/after-delombok/FieldNameConstantsBasic.java +++ b/test/transform/resource/after-delombok/FieldNameConstantsBasic.java @@ -1,8 +1,12 @@ public class FieldNameConstantsBasic { - protected static final java.lang.String FIELD_I_AM_A_DVD_PLAYER = "iAmADvdPlayer"; - public static final java.lang.String FIELD_BUT_PRINT_ME_PLEASE = "butPrintMePlease"; String iAmADvdPlayer; int $skipMe; static double skipMeToo; + int andMe; String butPrintMePlease; + @java.lang.SuppressWarnings("all") + static final class Fields { + public static final java.lang.String butPrintMePlease = "butPrintMePlease"; + public static final java.lang.String iAmADvdPlayer = "iAmADvdPlayer"; + } } diff --git a/test/transform/resource/after-delombok/FieldNameConstantsConfigKeys.java b/test/transform/resource/after-delombok/FieldNameConstantsConfigKeys.java index c71b9264..4c2a53e3 100644 --- a/test/transform/resource/after-delombok/FieldNameConstantsConfigKeys.java +++ b/test/transform/resource/after-delombok/FieldNameConstantsConfigKeys.java @@ -1,4 +1,7 @@ public class FieldNameConstantsConfigKeys { - public static final java.lang.String I_AM_A_DVD_PLAYER_SFX = "iAmADvdPlayer"; String iAmADvdPlayer; -} + @java.lang.SuppressWarnings("all") + public static final class Foobar { + public static final java.lang.String iAmADvdPlayer = "iAmADvdPlayer"; + } +}
\ No newline at end of file diff --git a/test/transform/resource/after-delombok/FieldNameConstantsEnum.java b/test/transform/resource/after-delombok/FieldNameConstantsEnum.java new file mode 100644 index 00000000..c3fbf0c2 --- /dev/null +++ b/test/transform/resource/after-delombok/FieldNameConstantsEnum.java @@ -0,0 +1,10 @@ +public class FieldNameConstantsEnum { + String iAmADvdPlayer; + int $dontSkipMe; + static double alsoDontSkipMe; + int butSkipMe; + @java.lang.SuppressWarnings("all") + public enum TypeTest { + iAmADvdPlayer, $dontSkipMe, alsoDontSkipMe; + } +} diff --git a/test/transform/resource/after-delombok/FieldNameConstantsWeird.java b/test/transform/resource/after-delombok/FieldNameConstantsWeird.java deleted file mode 100644 index 6940f628..00000000 --- a/test/transform/resource/after-delombok/FieldNameConstantsWeird.java +++ /dev/null @@ -1,6 +0,0 @@ -public class FieldNameConstantsWeird { - public static final java.lang.String FIELD_AZ = "A"; - String iAmADvdPlayer; - String X; - String A; -} diff --git a/test/transform/resource/after-ecj/BuilderSingularToBuilderWithNull.java b/test/transform/resource/after-ecj/BuilderSingularToBuilderWithNull.java new file mode 100644 index 00000000..7265e17a --- /dev/null +++ b/test/transform/resource/after-ecj/BuilderSingularToBuilderWithNull.java @@ -0,0 +1,57 @@ +import lombok.Singular; +@lombok.Builder(toBuilder = true) class BuilderSingularToBuilderWithNull { + public static @java.lang.SuppressWarnings("all") class BuilderSingularToBuilderWithNullBuilder { + private @java.lang.SuppressWarnings("all") java.util.ArrayList<String> elems; + @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNullBuilder() { + super(); + } + public @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNullBuilder elem(String elem) { + if ((this.elems == null)) + this.elems = new java.util.ArrayList<String>(); + this.elems.add(elem); + return this; + } + public @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNullBuilder elems(java.util.Collection<? extends String> elems) { + if ((this.elems == null)) + this.elems = new java.util.ArrayList<String>(); + this.elems.addAll(elems); + return this; + } + public @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNullBuilder clearElems() { + if ((this.elems != null)) + this.elems.clear(); + return this; + } + public @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNull build() { + java.util.List<String> elems; + switch (((this.elems == null) ? 0 : this.elems.size())) { + case 0 : + elems = java.util.Collections.emptyList(); + break; + case 1 : + elems = java.util.Collections.singletonList(this.elems.get(0)); + break; + default : + elems = java.util.Collections.unmodifiableList(new java.util.ArrayList<String>(this.elems)); + } + return new BuilderSingularToBuilderWithNull(elems); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (("BuilderSingularToBuilderWithNull.BuilderSingularToBuilderWithNullBuilder(elems=" + this.elems) + ")"); + } + } + private @Singular java.util.List<String> elems; + public static void test() { + new BuilderSingularToBuilderWithNull(null).toBuilder(); + } + @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNull(final java.util.List<String> elems) { + super(); + this.elems = elems; + } + public static @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNullBuilder builder() { + return new BuilderSingularToBuilderWithNullBuilder(); + } + public @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNullBuilder toBuilder() { + return new BuilderSingularToBuilderWithNullBuilder().elems(((this.elems == null) ? java.util.Collections.emptyList() : this.elems)); + } +} diff --git a/test/transform/resource/after-ecj/BuilderWithToBuilder.java b/test/transform/resource/after-ecj/BuilderWithToBuilder.java index d304293c..b9cc27dd 100644 --- a/test/transform/resource/after-ecj/BuilderWithToBuilder.java +++ b/test/transform/resource/after-ecj/BuilderWithToBuilder.java @@ -74,7 +74,7 @@ import lombok.Builder; return new BuilderWithToBuilderBuilder<T>(); } public @java.lang.SuppressWarnings("all") BuilderWithToBuilderBuilder<T> toBuilder() { - return new BuilderWithToBuilderBuilder<T>().one(this.mOne).two(this.mTwo).foo(BuilderWithToBuilder.rrr(this)).bars(this.bars); + return new BuilderWithToBuilderBuilder<T>().one(this.mOne).two(this.mTwo).foo(BuilderWithToBuilder.rrr(this)).bars(((this.bars == null) ? java.util.Collections.emptyList() : this.bars)); } } @lombok.experimental.Accessors(prefix = "m") class ConstructorWithToBuilder<T> { diff --git a/test/transform/resource/after-ecj/FieldNameConstantsBasic.java b/test/transform/resource/after-ecj/FieldNameConstantsBasic.java index f77203ba..674dd602 100644 --- a/test/transform/resource/after-ecj/FieldNameConstantsBasic.java +++ b/test/transform/resource/after-ecj/FieldNameConstantsBasic.java @@ -1,15 +1,23 @@ import lombok.experimental.FieldNameConstants; import lombok.AccessLevel; -public @FieldNameConstants class FieldNameConstantsBasic { - public static final java.lang.String FIELD_BUT_PRINT_ME_PLEASE = "butPrintMePlease"; - protected static final java.lang.String FIELD_I_AM_A_DVD_PLAYER = "iAmADvdPlayer"; - @FieldNameConstants(level = AccessLevel.PROTECTED) String iAmADvdPlayer; +public @FieldNameConstants(level = AccessLevel.PACKAGE) class FieldNameConstantsBasic { + static final @java.lang.SuppressWarnings("all") class Fields { + public static final java.lang.String butPrintMePlease = "butPrintMePlease"; + public static final java.lang.String iAmADvdPlayer = "iAmADvdPlayer"; + <clinit>() { + } + private @java.lang.SuppressWarnings("all") Fields() { + super(); + } + } + String iAmADvdPlayer; int $skipMe; static double skipMeToo; + @FieldNameConstants.Exclude int andMe; String butPrintMePlease; <clinit>() { } public FieldNameConstantsBasic() { super(); } -} +}
\ No newline at end of file diff --git a/test/transform/resource/after-ecj/FieldNameConstantsConfigKeys.java b/test/transform/resource/after-ecj/FieldNameConstantsConfigKeys.java index 44629ee5..7eb97364 100644 --- a/test/transform/resource/after-ecj/FieldNameConstantsConfigKeys.java +++ b/test/transform/resource/after-ecj/FieldNameConstantsConfigKeys.java @@ -1,9 +1,14 @@ public @lombok.experimental.FieldNameConstants class FieldNameConstantsConfigKeys { - public static final java.lang.String I_AM_A_DVD_PLAYER_SFX = "iAmADvdPlayer"; - String iAmADvdPlayer; - <clinit>() { + public static final @java.lang.SuppressWarnings("all") class Foobar { + public static final java.lang.String iAmADvdPlayer = "iAmADvdPlayer"; + <clinit>() { + } + private @java.lang.SuppressWarnings("all") Foobar() { + super(); + } } + String iAmADvdPlayer; public FieldNameConstantsConfigKeys() { super(); } -} +}
\ No newline at end of file diff --git a/test/transform/resource/after-ecj/FieldNameConstantsEnum.java b/test/transform/resource/after-ecj/FieldNameConstantsEnum.java new file mode 100644 index 00000000..053a6e7a --- /dev/null +++ b/test/transform/resource/after-ecj/FieldNameConstantsEnum.java @@ -0,0 +1,23 @@ +import lombok.experimental.FieldNameConstants; +import lombok.AccessLevel; +public @FieldNameConstants(onlyExplicitlyIncluded = true,asEnum = true,innerTypeName = "TypeTest") class FieldNameConstantsEnum { + public @java.lang.SuppressWarnings("all") enum TypeTest { + iAmADvdPlayer(), + $dontSkipMe(), + alsoDontSkipMe(), + private @java.lang.SuppressWarnings("all") TypeTest() { + super(); + } + <clinit>() { + } + } + @FieldNameConstants.Include String iAmADvdPlayer; + @FieldNameConstants.Include int $dontSkipMe; + static @FieldNameConstants.Include double alsoDontSkipMe; + int butSkipMe; + <clinit>() { + } + public FieldNameConstantsEnum() { + super(); + } +}
\ No newline at end of file diff --git a/test/transform/resource/after-ecj/FieldNameConstantsWeird.java b/test/transform/resource/after-ecj/FieldNameConstantsWeird.java deleted file mode 100644 index 9958f664..00000000 --- a/test/transform/resource/after-ecj/FieldNameConstantsWeird.java +++ /dev/null @@ -1,13 +0,0 @@ -import lombok.experimental.FieldNameConstants; -import lombok.AccessLevel; -public @FieldNameConstants class FieldNameConstantsWeird { - public static final java.lang.String FIELD_AZ = "A"; - @FieldNameConstants(level = AccessLevel.NONE) String iAmADvdPlayer; - @FieldNameConstants(prefix = "") String X; - @FieldNameConstants(suffix = "Z") String A; - <clinit>() { - } - public FieldNameConstantsWeird() { - super(); - } -} diff --git a/test/transform/resource/before/BuilderSingularToBuilderWithNull.java b/test/transform/resource/before/BuilderSingularToBuilderWithNull.java new file mode 100644 index 00000000..92a102cb --- /dev/null +++ b/test/transform/resource/before/BuilderSingularToBuilderWithNull.java @@ -0,0 +1,10 @@ +import lombok.Singular; + +@lombok.Builder(toBuilder = true) +class BuilderSingularToBuilderWithNull { + @Singular private java.util.List<String> elems; + + public static void test() { + new BuilderSingularToBuilderWithNull(null).toBuilder(); + } +} diff --git a/test/transform/resource/before/FieldNameConstantsBasic.java b/test/transform/resource/before/FieldNameConstantsBasic.java index 1bc15d84..2842dea3 100644 --- a/test/transform/resource/before/FieldNameConstantsBasic.java +++ b/test/transform/resource/before/FieldNameConstantsBasic.java @@ -1,11 +1,11 @@ import lombok.experimental.FieldNameConstants; import lombok.AccessLevel; -@FieldNameConstants +@FieldNameConstants(level = AccessLevel.PACKAGE) public class FieldNameConstantsBasic { - @FieldNameConstants(level = AccessLevel.PROTECTED) String iAmADvdPlayer; int $skipMe; static double skipMeToo; + @FieldNameConstants.Exclude int andMe; String butPrintMePlease; } diff --git a/test/transform/resource/before/FieldNameConstantsConfigKeys.java b/test/transform/resource/before/FieldNameConstantsConfigKeys.java index ab8e3091..5595222c 100644 --- a/test/transform/resource/before/FieldNameConstantsConfigKeys.java +++ b/test/transform/resource/before/FieldNameConstantsConfigKeys.java @@ -1,5 +1,4 @@ -//CONF: lombok.fieldNameConstants.prefix = -//CONF: lombok.fieldNameConstants.suffix = _SFX +//CONF: lombok.fieldNameConstants.innerTypeName = Foobar @lombok.experimental.FieldNameConstants public class FieldNameConstantsConfigKeys { diff --git a/test/transform/resource/before/FieldNameConstantsEnum.java b/test/transform/resource/before/FieldNameConstantsEnum.java new file mode 100644 index 00000000..d56bb280 --- /dev/null +++ b/test/transform/resource/before/FieldNameConstantsEnum.java @@ -0,0 +1,13 @@ +import lombok.experimental.FieldNameConstants; +import lombok.AccessLevel; + +@FieldNameConstants(onlyExplicitlyIncluded = true, asEnum = true, innerTypeName = "TypeTest") +public class FieldNameConstantsEnum { + @FieldNameConstants.Include + String iAmADvdPlayer; + @FieldNameConstants.Include + int $dontSkipMe; + @FieldNameConstants.Include + static double alsoDontSkipMe; + int butSkipMe; +} diff --git a/test/transform/resource/before/FieldNameConstantsWeird.java b/test/transform/resource/before/FieldNameConstantsWeird.java deleted file mode 100644 index 74ec299a..00000000 --- a/test/transform/resource/before/FieldNameConstantsWeird.java +++ /dev/null @@ -1,12 +0,0 @@ -import lombok.experimental.FieldNameConstants; -import lombok.AccessLevel; - -@FieldNameConstants -public class FieldNameConstantsWeird { - @FieldNameConstants(level = AccessLevel.NONE) - String iAmADvdPlayer; - @FieldNameConstants(prefix = "") - String X; - @FieldNameConstants(suffix = "Z") - String A; -} diff --git a/test/transform/resource/messages-delombok/FieldNameConstantsWeird.java.messages b/test/transform/resource/messages-delombok/FieldNameConstantsWeird.java.messages deleted file mode 100644 index 02a38d58..00000000 --- a/test/transform/resource/messages-delombok/FieldNameConstantsWeird.java.messages +++ /dev/null @@ -1 +0,0 @@ -9 Not generating constant for this field: The name of the constant would be equal to the name of this field. diff --git a/test/transform/resource/messages-ecj/FieldNameConstantsWeird.java.messages b/test/transform/resource/messages-ecj/FieldNameConstantsWeird.java.messages deleted file mode 100644 index 02a38d58..00000000 --- a/test/transform/resource/messages-ecj/FieldNameConstantsWeird.java.messages +++ /dev/null @@ -1 +0,0 @@ -9 Not generating constant for this field: The name of the constant would be equal to the name of this field. diff --git a/website/extra/htaccess b/website/extra/htaccess index 2d5b6b2a..f2abad2d 100644 --- a/website/extra/htaccess +++ b/website/extra/htaccess @@ -4,62 +4,62 @@ RewriteRule ^$ /main.html [L,END] RewriteRule ^(index|home)(\.html)?$ / [NC,R=301] RewriteRule ^setup/overview$ /setup/main.html [L,END] -RewriteRule ^setup/main(\.html)?$ /setup/overview [NC,R=301] -RewriteRule ^setup/index(\.html)?$ /setup/overview [NC,R=301] -RewriteRule ^setup(/(overview)?)?$ /setup/overview [NC,R=301] +RewriteRule ^setup/main(\.html)?/?$ /setup/overview [NC,R=301] +RewriteRule ^setup/index(\.html)?/?$ /setup/overview [NC,R=301] +RewriteRule ^setup(/(overview/?)?)?$ /setup/overview [NC,R=301] RewriteRule ^changelog$ /changelog.html [L,END] -RewriteRule ^changelog(\.html)?$ /changelog [NC,R=301] +RewriteRule ^changelog(\.html)?/?$ /changelog [NC,R=301] RewriteRule ^credits$ /credits.html [L,END] -RewriteRule ^credits(\.html)?$ /credits [NC,R=301] +RewriteRule ^credits(\.html)?/?$ /credits [NC,R=301] RewriteRule ^download$ /download.html [L,END] -RewriteRule ^download(\.html)?$ /download [NC,R=301] +RewriteRule ^download(\.html)?/?$ /download [NC,R=301] RewriteRule ^download-edge$ /download-edge.html [L,END] -RewriteRule ^download-edge(\.html)?$ /download-edge [NC,R=301] +RewriteRule ^download-edge(\.html)?/?$ /download-edge [NC,R=301] RewriteRule ^all-versions$ /all-versions.html [L,END] -RewriteRule ^all-versions(\.html)?$ /all-versions [NC,R=301] +RewriteRule ^all-versions(\.html)?/?$ /all-versions [NC,R=301] RewriteRule ^all-versions$ /all-versions.html [L,END] -RewriteRule ^all-versions(\.html)?$ /all-versions [NC,R=301] +RewriteRule ^all-versions(\.html)?/?$ /all-versions [NC,R=301] RewriteRule ^disable-checked-exceptions$ /disable-checked-exceptions.html [L,END] -RewriteRule ^disable-checked-exceptions(\.html)?$ /disable-checked-exceptions [NC,R=301] +RewriteRule ^disable-checked-exceptions(\.html)?/?$ /disable-checked-exceptions [NC,R=301] RewriteRule ^supporters$ /supporters.html [L,END] -RewriteRule ^supporters(.html)?$ /supporters [NC,R=301] +RewriteRule ^supporters(.html)?/?$ /supporters [NC,R=301] RewriteRule ^order-license-info$ /order-license-info.html [L,END] -RewriteRule ^order-?license-?info(.html)?$ /order-license-info [NC,R=301] +RewriteRule ^order-?license-?info(.html)?/?$ /order-license-info [NC,R=301] RewriteRule ^order-license$ /order-license.html [L,END] -RewriteRule ^order-?license(.html)?$ /order-license [NC,R=301] +RewriteRule ^order-?license(.html)?/?$ /order-license [NC,R=301] RewriteRule ^contributing/index$ /contributing/index.html [L,END] -RewriteRule ^contributing(\.html)?$ /contributing/index [NC,R=301] -RewriteRule ^contributing/index(\.html)?$ /contributing/index [NC,R=301] +RewriteRule ^contributing(\.html)?/?$ /contributing/index [NC,R=301] +RewriteRule ^contributing/index(\.html)?/?$ /contributing/index [NC,R=301] RewriteRule ^contributing/contributing$ /contributing/contributing.html [L,END] -RewriteRule ^contributing/contributing(\.html)?$ /contributing/contributing [NC,R=301] +RewriteRule ^contributing/contributing(\.html)?/?$ /contributing/contributing [NC,R=301] RewriteRule ^contributing/lombok-execution-path$ /contributing/lombok-execution-path.html [L,END] -RewriteRule ^contributing/lombok-execution-path(\.html)?$ /contributing/lombok-execution-path [NC,R=301] +RewriteRule ^contributing/lombok-execution-path(\.html)?/?$ /contributing/lombok-execution-path [NC,R=301] <#list setupPages as pg> RewriteRule ^setup/${pg?no_esc}$ /setup/${pg?no_esc}.html [L,END] -RewriteRule ^setup/${pg?no_esc}(\.html)?$ /setup/${pg?no_esc} [NC,R=301] +RewriteRule ^setup/${pg?no_esc}(\.html)?/?$ /setup/${pg?no_esc} [NC,R=301] </#list> RewriteRule ^features/all$ /features/index.html [L,END] -RewriteRule ^features(/all)?$ /features/all [NC,R=301] -RewriteRule ^features/index(\.html)?$ /features/all [NC,R=301] +RewriteRule ^features(/all)?/?$ /features/all [NC,R=301] +RewriteRule ^features/index(\.html)?/?$ /features/all [NC,R=301] <#list featurePages as pg> RewriteRule ^features/${pg?no_esc}$ /features/${pg?no_esc}.html [L,END] -RewriteRule ^features/${pg?no_esc}(\.html)?$ /features/${pg?no_esc} [NC,R=301] +RewriteRule ^features/${pg?no_esc}(\.html)?/?$ /features/${pg?no_esc} [NC,R=301] </#list> -RewriteRule ^features/experimental/Builder(\.html)?$ /features/Builder [NC,R=301] -RewriteRule ^features/experimental/Value(\.html)?$ /features/Value [NC,R=301] -RewriteRule ^features/experimental/var(\.html)?$ /features/var [NC,R=301] +RewriteRule ^features/experimental/Builder(\.html)?/?$ /features/Builder [NC,R=301] +RewriteRule ^features/experimental/Value(\.html)?/?$ /features/Value [NC,R=301] +RewriteRule ^features/experimental/var(\.html)?/?$ /features/var [NC,R=301] RewriteRule ^features/experimental/all$ /features/experimental/index.html [L,END] -RewriteRule ^features/experimental(/all)?$ /features/experimental/all [NC,R=301] -RewriteRule ^features/experimental/index(\.html)?$ /features/experimental/all [NC,R=301] +RewriteRule ^features/experimental(/all)?/?$ /features/experimental/all [NC,R=301] +RewriteRule ^features/experimental/index(\.html)?/?$ /features/experimental/all [NC,R=301] <#list experimentalPages as pg> RewriteRule ^features/experimental/${pg?no_esc}$ /features/experimental/${pg?no_esc}.html [L,END] -RewriteRule ^features/experimental/${pg?no_esc}(\.html)?$ /features/experimental/${pg?no_esc} [NC,R=301] +RewriteRule ^features/experimental/${pg?no_esc}(\.html)?/?$ /features/experimental/${pg?no_esc} [NC,R=301] </#list> diff --git a/website/templates/_scaffold.html b/website/templates/_scaffold.html index 7acfb6b6..65b2fb7f 100644 --- a/website/templates/_scaffold.html +++ b/website/templates/_scaffold.html @@ -111,7 +111,7 @@ ga('send', 'pageview'); </a> </li> <li class="dropdown"> - <a href="/setup/overview" class="smallOnly"><span>Install</span></a> + <a href="/setup/overview" class="smallOnly"><span>How to use</span></a> <a class="dropdown-toggle pointer wideOnly" data-toggle="dropdown">Install<span class="caret"></span></a> <ul class="dropdown-menu" aria-labelledby="themes"> <#list setupTargets as cat, tgtList> diff --git a/website/templates/features/Builder.html b/website/templates/features/Builder.html index 4693d222..bc03749e 100644 --- a/website/templates/features/Builder.html +++ b/website/templates/features/Builder.html @@ -129,6 +129,27 @@ </p> </@f.featureSection> + <@f.featureSection> + <h3 id="jackson"><a name="jackson">With Jackson</a></h3> + <p> + You can customize parts of your builder, for example adding another method to the builder class, or annotating a method in the builder class, by making the builder class yourself. Lombok will generate everything that you do not manually add, and put it into this builder class. For example, if you are trying to configure <a href="https://github.com/FasterXML/jackson">jackson</a> to use a specific subtype for a collection, you can write something like:<div class="snippet"><div class="java" align="left"><pre> +@Value @Builder +@JsonDeserialize(builder = JacksonExample.JacksonExampleBuilder.class) +public class JacksonExample { + @Singular private List<Foo> foos; + + @JsonPOJOBuilder(withPrefix = "") + public static class JacksonExampleBuilder implements JacksonExampleBuilderMeta { + } + + private interface JacksonExampleBuilderMeta { + @JsonDeserialize(contentAs = FooImpl.class) JacksonExampleBuilder foos(List<? extends Foo> foos) + } +} +</pre></div></div> + </p> + </@f.featureSection> + <@f.snippets name="Builder" /> <@f.confKeys> diff --git a/website/templates/features/experimental/FieldNameConstants.html b/website/templates/features/experimental/FieldNameConstants.html index c5e57195..a6fac99d 100644 --- a/website/templates/features/experimental/FieldNameConstants.html +++ b/website/templates/features/experimental/FieldNameConstants.html @@ -4,6 +4,8 @@ <@f.history> <p> @FieldNameConstants was introduced as experimental feature in lombok v1.16.22. + </p><p> + @FieldNameConstants was redesigned in lombok v1.18.4. </p> </@f.history> @@ -18,11 +20,11 @@ <@f.overview> <p> - The <code>@FieldNameConstants</code> annotation generates string constants (fields marked <code>public static final</code>, of type <code>java.lang.String</code>) containing the field's name, as a string. This is useful for various marshalling and serialization frameworks. The constant field by default is named <code>FIELD_<em>NAME_OF_FIELD</em></code>, where <em>NAME_OF_FIELD</em> is the same name as the field it represents, except with all uppercase letters, with underscores in front of the uppercase letters in the original field. The prefix (and suffix) is configurable on the <code>@FieldNameConstants</code> annotation. + The <code>@FieldNameConstants</code> annotation generates an inner type which contains 1 constant for each field in your class; either string constants (fields marked <code>public static final</code>, of type <code>java.lang.String</code>) or if you prefer, an enum type with 1 value for each field. <code>@FieldNameConstants</code> is useful for various marshalling and serialization frameworks. The constant field (whether enum value or string constant) always has the exact same name as the field, capitalization and all. </p><p> - The <code>public</code> access modifier can be changed via the parameter <code>level = AccessLevel.PACKAGE</code> for example. You can force a field to be skipped by supplying <code>level = AccessLevel.NONE</code>. + The generated inner type is by default called <code>Fields</code> and is <code>public</code>. You can modify this via <code>@FieldNameConstants(innerTypeName = "FieldNames", access = AccessLevel.PACKAGE)</code> for example. The default inner type name can also be modified via configuration key <code>lombok.fieldNameConstants.innerTypeName</code>. The generated fields are always <code>public</code>. </p><p> - Can be applied to classes (in which case every field gets a constant), or to an individual field. + Must be applied to classes (or enums, though you'd rarely want to do that). By default includes all non-transient, non-static fields. You can use <code>@FieldNameConstants.Include</code> in fields + <code>@FieldNameConstants(onlyExplicitlyIncluded = true)</code>, or <code>@FieldNameConstants.Exclude</code> for more fine-grained control. </p> </@f.overview> @@ -34,21 +36,19 @@ </dt><dd> Lombok will flag any usage of <code>@FieldDefaults</code> as a warning or error if configured. </dd><dt> - <code>lombok.fieldNameConstants.prefix</code> = <em>a string</em> (default: 'PREFIX_') - </dt><dd> - Lombok will generate the name for each fieldconstant by constant-casing the field name and then prefixing this string to the result. - </dd><dt> - <code>lombok.fieldNameConstants.suffix</code> = <em>a string</em> (default: '' - the blank string) + <code>lombok.fieldNameConstants.innerTypeName</code> = <em>a string</em> (default: 'Fields') </dt><dd> - Lombok will generate the name for each fieldconstant by constant-casing the field name and then suffixing this string to the result. + The name of the inner type generated by lombok can be controlled with this configuration key. </dd> </@f.confKeys> <@f.smallPrint> <p> - Like other lombok handlers that touch fields, any field whose name starts with a dollar (<code>$</code>) symbol is skipped entirely. Such a field will not be modified at all. Static fields are also skipped. + From lombok v1.16.22 to lombok v1.18.2, this feature generated constants inside the type directly; the name of these fields would for example turn field <code>exampleFieldName</code> into <code>public static final String FIELD_EXAMPLE_FIELD_NAME = "exampleFieldName";</code>. The prefix and suffix (here, <code>FIELD_</code>, and the empty string) were configurable. Starting with lombok v1.18.4 this feature has been redesigned into generating an inner type as described above. + </p><p> + Any parameters of lombok annotations that take strings need to be supplied actual string literals; you cannot have references to constants like those generated by <code>@FieldNameConstants</code>. If you'd like to use <code>@FieldNameConstants</code> to for example fill in the <code>of</code> and/or <code>exclude</code> parameters of <code>@ToString</code> and similar lombok annotations, use the <code>@ToString.Include</code> / <code>@ToString.Exclude</code> etc system instead; these are described at the feature pages for those features. </p><p> - The annotation can itself be used to set prefix/suffix. If you do so, it overrides the <code>lombok.fieldNameConstants.prefix/suffix</code> config key. Example: <code>@FieldNameConstants(prefix = "")</code>: This would generate for field <code>helloWorld</code> a constant named <code>HELLO_WORLD</code> instead of the default <code>FIELD_HELLO_WORLD</code>. + Like other lombok handlers that touch fields, any field whose name starts with a dollar (<code>$</code>) symbol is skipped entirely. Such a field will not be modified at all. Static fields are also skipped. </p> </@f.smallPrint> </@f.scaffold> diff --git a/website/templates/setup/gradle.html b/website/templates/setup/gradle.html index 7cb4af0d..41d90dcc 100644 --- a/website/templates/setup/gradle.html +++ b/website/templates/setup/gradle.html @@ -15,7 +15,7 @@ </p><p> Note, to tell the <code>gradle-lombok</code> plugin to use the latest version of lombok, you need to explicitly tell it about the latest version number and the SHA-256. For our current latest version, put this in your <code>build.gradle</code> file:<pre> lombok { - version = ${version} + version = '${version}' sha256 = "" }</pre> </p> diff --git a/website/usageExamples/LogExample_post.jpage b/website/usageExamples/LogExample_post.jpage index eab3b046..78c0fdd9 100644 --- a/website/usageExamples/LogExample_post.jpage +++ b/website/usageExamples/LogExample_post.jpage @@ -2,7 +2,7 @@ public class LogExample { private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName()); public static void main(String... args) { - log.error("Something's wrong here"); + log.severe("Something's wrong here"); } } diff --git a/website/usageExamples/LogExample_pre.jpage b/website/usageExamples/LogExample_pre.jpage index ba27dd27..9fa1e91c 100644 --- a/website/usageExamples/LogExample_pre.jpage +++ b/website/usageExamples/LogExample_pre.jpage @@ -5,7 +5,7 @@ import lombok.extern.slf4j.Slf4j; public class LogExample { public static void main(String... args) { - log.error("Something's wrong here"); + log.severe("Something's wrong here"); } } |