diff -r 0df446056874 java.hints/src/org/netbeans/modules/java/hints/Bundle.properties
--- a/java.hints/src/org/netbeans/modules/java/hints/Bundle.properties Sat May 02 11:43:41 2009 +0200
+++ b/java.hints/src/org/netbeans/modules/java/hints/Bundle.properties Tue May 05 22:08:18 2009 +0100
@@ -263,6 +263,12 @@
ERR_SynchronizationOnNonFinalField=Synchronization on non-final field
DN_SynchronizationOnNonFinalField=Synchronization on non-final field
+DSC_SerialVersionUID=serialVersionUID not defined
+DN_SerialVersionUID=serialVersionUID not defined
+ERR_SerialVersionUID=serialVersionUID not defined
+HINT_SerialVersionUID=Add default serialVersionUID
+HINT_SerialVersionUID_Generated=Add generated serialVersionUID
+
HINT_SuspiciousCall=Suspicious call to {0}:\nExpected type {2}, actual type {1}
HINT_SuspiciousCallIncompatibleTypes=Suspicious call to {0}:\nGiven object cannot contain instances of {1} (expected {2})
DN_CollectionRemove=Suspicous method call
diff -r 0df446056874 java.hints/src/org/netbeans/modules/java/hints/SerialVersionUID.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java.hints/src/org/netbeans/modules/java/hints/SerialVersionUID.java Tue May 05 22:08:18 2009 +0100
@@ -0,0 +1,212 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ */
+package org.netbeans.modules.java.hints;
+
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.Tree.Kind;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.TreePath;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.Modifier;
+import static javax.lang.model.element.Modifier.*;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.GeneratorUtilities;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.JavaSource.Phase;
+import org.netbeans.api.java.source.Task;
+import org.netbeans.api.java.source.TreeMaker;
+import org.netbeans.api.java.source.TreePathHandle;
+import org.netbeans.api.java.source.WorkingCopy;
+import org.netbeans.modules.java.editor.codegen.GeneratorUtils;
+import org.netbeans.modules.java.hints.spi.AbstractHint;
+import org.netbeans.modules.java.hints.spi.support.FixFactory;
+import org.netbeans.spi.editor.hints.ChangeInfo;
+import org.netbeans.spi.editor.hints.ErrorDescription;
+import org.netbeans.spi.editor.hints.ErrorDescriptionFactory;
+import org.netbeans.spi.editor.hints.Fix;
+import org.openide.util.NbBundle;
+
+/**
+ * @author Michal Hlavac
+ * @author Samuel Halliday
+ *
+ * @see RFE 70746
+ * @see Original Implementation Source Code
+ */
+public class SerialVersionUID extends AbstractHint {
+
+ private static final String SERIAL = "serial"; //NOI18N
+ private static final String SVUID = "serialVersionUID"; //NOI18N
+ private static final String SERIALIZABLE = "java.io.Serializable"; //NOI18N
+ private final AtomicBoolean cancel = new AtomicBoolean();
+
+ public SerialVersionUID() {
+ super(true, false, AbstractHint.HintSeverity.WARNING);
+ }
+
+ @Override
+ public String getDescription() {
+ return NbBundle.getMessage(getClass(), "DSC_SerialVersionUID"); //NOI18N
+ }
+
+ public Set getTreeKinds() {
+ return EnumSet.of(Kind.CLASS);
+ }
+
+ public List run(CompilationInfo info, TreePath treePath) {
+ if (treePath == null || treePath.getLeaf().getKind() != Kind.CLASS) {
+ return null;
+ }
+ cancel.set(false);
+ TypeElement type = (TypeElement) info.getTrees().getElement(treePath);
+ if (type == null || type.getKind() == ElementKind.INTERFACE || !isSerializable(type) || hasSerialVersionUID(type) || hasSuppressWarning(type, SERIAL)) {
+ return null;
+ }
+ // Contrary to popular belief, abstract classes *should* define serialVersionUID,
+ // according to the documentation of Serializable. It refers to "all classes".
+ List fixes = new ArrayList();
+ fixes.add(new FixImpl(TreePathHandle.create(treePath, info), false));
+ // fixes.add(new FixImpl(TreePathHandle.create(treePath, info), true));
+ fixes.addAll(FixFactory.createSuppressWarnings(info, treePath, SERIAL));
+
+ String desc = NbBundle.getMessage(getClass(), "ERR_SerialVersionUID"); //NOI18N
+ int[] span = info.getTreeUtilities().findNameSpan((ClassTree) treePath.getLeaf());
+ ErrorDescription ed = ErrorDescriptionFactory.createErrorDescription(getSeverity().toEditorSeverity(), desc, fixes, info.getFileObject(), span[0], span[1]);
+ if (cancel.get()) {
+ return null;
+ }
+ return Collections.singletonList(ed);
+ }
+
+ public String getId() {
+ return getClass().getName();
+ }
+
+ public String getDisplayName() {
+ return NbBundle.getMessage(getClass(), "DN_SerialVersionUID");//NOI18N
+ }
+
+ public void cancel() {
+ cancel.set(true);
+ }
+
+ private static class FixImpl implements Fix, Task {
+
+ private final TreePathHandle handle;
+ private final boolean generated;
+
+ /**
+ * @param handle to the CLASS
+ * @param generated true will insert a generated value, false will use a default
+ */
+ public FixImpl(TreePathHandle handle, boolean generated) {
+ this.handle = handle;
+ this.generated = generated;
+ if (generated) {
+ throw new UnsupportedOperationException("TODO: implement");
+ }
+ }
+
+ public String getText() {
+ if (generated) {
+ return NbBundle.getMessage(getClass(), "HINT_SerialVersionUID_Generated");//NOI18N
+ }
+ return NbBundle.getMessage(getClass(), "HINT_SerialVersionUID");//NOI18N
+ }
+
+ public ChangeInfo implement() throws Exception {
+ JavaSource js = JavaSource.forFileObject(handle.getFileObject());
+ js.runModificationTask(this).commit();
+ return null;
+ }
+
+ public void run(WorkingCopy copy) throws Exception {
+ if (copy.toPhase(Phase.RESOLVED).compareTo(Phase.RESOLVED) < 0) {
+ return;
+ }
+ TreePath treePath = handle.resolve(copy);
+ if (treePath == null || treePath.getLeaf().getKind() != Kind.CLASS) {
+ return;
+ }
+ ClassTree classTree = (ClassTree) treePath.getLeaf();
+ TreeMaker make = copy.getTreeMaker();
+
+ // documentation recommends private
+ Set modifiers = EnumSet.of(PRIVATE, STATIC, FINAL);
+ VariableTree svuid = make.Variable(make.Modifiers(modifiers), SVUID, make.Identifier("long"), make.Literal(1L)); //NO18N
+
+ ClassTree decl = GeneratorUtilities.get(copy).insertClassMember(classTree, svuid);
+ copy.rewrite(classTree, decl);
+ }
+ }
+
+ private boolean isSerializable(TypeElement type) {
+ for (TypeElement t : GeneratorUtils.getAllParents(type)) {
+ if (t.getKind() == ElementKind.INTERFACE && t.getQualifiedName().contentEquals(SERIALIZABLE)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean hasSerialVersionUID(TypeElement type) {
+ for (VariableElement e : ElementFilter.fieldsIn(type.getEnclosedElements())) {
+ if (e.getSimpleName().contentEquals(SVUID)) {
+ Set modifiers = e.getModifiers();
+ // documentation says ANY-ACCESS-MODIFIER static final long serialVersionUID
+ if (modifiers.containsAll(EnumSet.of(STATIC, FINAL))) {
+ TypeMirror t = e.asType();
+ if (t.getKind() != null && t.getKind() == TypeKind.LONG) {
+ return true;
+ }
+ }
+
+ }
+ }
+ return false;
+ }
+
+ private static boolean hasSuppressWarning(TypeElement type, String warning) {
+ SuppressWarnings annotation = type.getAnnotation(SuppressWarnings.class);
+ if (annotation != null) {
+ for (String val : annotation.value()) {
+ if (val.equals(warning)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
diff -r 0df446056874 java.hints/src/org/netbeans/modules/java/hints/resources/layer.xml
--- a/java.hints/src/org/netbeans/modules/java/hints/resources/layer.xml Sat May 02 11:43:41 2009 +0200
+++ b/java.hints/src/org/netbeans/modules/java/hints/resources/layer.xml Tue May 05 22:08:18 2009 +0100
@@ -140,6 +140,7 @@
+
diff -r 0df446056874 java.hints/test/unit/src/org/netbeans/modules/java/hints/SerialVersionUIDTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/java.hints/test/unit/src/org/netbeans/modules/java/hints/SerialVersionUIDTest.java Tue May 05 22:08:18 2009 +0100
@@ -0,0 +1,151 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ */
+package org.netbeans.modules.java.hints;
+
+import com.sun.source.util.TreePath;
+import java.util.List;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.modules.java.hints.errors.SuppressWarningsFixer;
+import org.netbeans.modules.java.hints.infrastructure.TreeRuleTestBase;
+import org.netbeans.spi.editor.hints.ErrorDescription;
+import org.netbeans.spi.editor.hints.Fix;
+import org.openide.util.NbBundle;
+import static org.junit.Assert.*;
+
+/**
+ * The following shell script was used to generate the code snippets
+ * cat test/unit/data/test/Test.java | tr '\n' ' ' | tr '\t' ' ' | sed -E 's| +| |g' | sed 's|"|\\"|g'
+ * @author Samuel Halliday
+ */
+public class SerialVersionUIDTest extends TreeRuleTestBase {
+
+ private final SerialVersionUID computer = new SerialVersionUID();
+ private static final String HINT_SUPPRESS = NbBundle.getMessage(SuppressWarningsFixer.class, "LBL_FIX_Suppress_Waning", "serial");
+ private static final String HINT_DEFAULT = NbBundle.getMessage(SerialVersionUID.class, "HINT_SerialVersionUID");
+ private static final String HINT_GENERATED = NbBundle.getMessage(SerialVersionUID.class, "HINT_SerialVersionUID_Generated");
+
+ public SerialVersionUIDTest(String name) {
+ super(name);
+ }
+
+ public void testSerialVersionUID1() throws Exception {
+ String test = "package test; import java.io.Serializable; public interface T|est implements Serializable { }";
+ performAnalysisTest(test);
+ }
+
+ public void testSerialVersionUID2() throws Exception {
+ String test = "package test; import java.io.Serializable; @SuppressWarnings(\"serial\") public class T|est implements Serializable { }";
+ performAnalysisTest(test);
+ }
+
+ public void testSerialVersionUID3() throws Exception {
+ String test = "package test; import java.io.Serializable; @SuppressWarnings(\"serial\") abstract public class T|est implements Serializable { }";
+ performAnalysisTest(test);
+ }
+
+ public void testSerialVersionUID4() throws Exception {
+ String test = "package test; import java.io.Serializable; public class Te|st implements Serializable { private static final long serialVersionUID = 1L; }";
+ performAnalysisTest(test);
+ }
+
+ public void testSerialVersionUID5() throws Exception {
+ String test = "package test; import java.io.Serializable; abstract public class Te|st implements Serializable { private static final long serialVersionUID = 1L; }";
+ performAnalysisTest(test);
+ }
+
+ public void testSerialVersionUIDSuppress1() throws Exception {
+ String test = "package test; import java.io.Serializable; public class Te|st implements Serializable { }";
+ String golden = "package test; import java.io.Serializable; @SuppressWarnings(\"serial\") public class Test implements Serializable { }";
+ performFixTest(test, golden, HINT_SUPPRESS);
+ }
+
+ public void testSerialVersionUIDSuppress2() throws Exception {
+ String test = "package test; import java.io.Serializable; abstract public class T|est implements Serializable { }";
+ String golden = "package test; import java.io.Serializable; @SuppressWarnings(\"serial\") abstract public class Test implements Serializable { }";
+ performFixTest(test, golden, HINT_SUPPRESS);
+ }
+
+ public void testSerialVersionUIDDefault1() throws Exception {
+ String test = "package test; import java.io.Serializable; public class Te|st implements Serializable { }";
+ String golden = "package test; import java.io.Serializable; public class Test implements Serializable { private static final long serialVersionUID = 1L; }";
+ performFixTest(test, golden, HINT_DEFAULT);
+ }
+
+ public void testSerialVersionUIDDefault2() throws Exception {
+ String test = "package test; import java.io.Serializable; abstract public class Te|st implements Serializable { }";
+ String golden = "package test; import java.io.Serializable; abstract public class Test implements Serializable { private static final long serialVersionUID = 1L; }";
+ performFixTest(test, golden, HINT_DEFAULT);
+ }
+
+ // test is single line source code for test.Test, | in the CLASS, space before, space after
+ // golden is the output to test against
+ private void performFixTest(String test, String golden, String hint) throws Exception {
+ int offset = test.indexOf("|");
+ assertTrue(offset != -1);
+ int end = test.indexOf(" ", offset) - 1;
+ assertTrue(end > 0);
+ int start = test.lastIndexOf(" ", offset) + 1;
+ assertTrue(start > 0);
+ performFixTest("test/Test.java",
+ test.replace("|", ""),
+ offset,
+ "0:" + start + "-0:" + end + ":verifier:" + NbBundle.getMessage(SerialVersionUID.class, "DSC_SerialVersionUID"),
+ hint,
+ golden);
+ }
+
+ // test is single line source code for test.Test, | in the CLASS, space before, space after
+ // completes successfully if there are no hints presented
+ private void performAnalysisTest(String test) throws Exception {
+ int offset = test.indexOf("|");
+ assertTrue(offset != -1);
+ performAnalysisTest("test/Test.java", test.replace("|", ""), offset);
+ }
+
+ @Override
+ protected List computeErrors(CompilationInfo info, TreePath path) {
+ return computer.run(info, path);
+ }
+
+ @Override
+ protected String toDebugString(CompilationInfo info, Fix f) {
+ return f.getText();
+ }
+// // uncomment to speed up development cycle
+// @Override
+// public void testIssue105979() throws Exception {
+// }
+//
+// @Override
+// public void testIssue108246() throws Exception {
+// }
+//
+// @Override
+// public void testIssue113933() throws Exception {
+// }
+//
+// @Override
+// public void testNoHintsForSimpleInitialize() throws Exception {
+// }
+}
\ No newline at end of file