You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by sk...@apache.org on 2021/07/20 08:46:40 UTC

[netbeans] branch master updated: Added Check regular expression window and hint. Modified Malformed Regexp Hint.

This is an automated email from the ASF dual-hosted git repository.

skygo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git


The following commit(s) were added to refs/heads/master by this push:
     new 0eff92f  Added Check regular expression window and hint. Modified Malformed Regexp Hint.
     new ea9ebc6  Merge pull request #2953 from mishrasandeep/netbeans-5561
0eff92f is described below

commit 0eff92f3770931017f6557cd1d84cd71b6fbe9ea
Author: mishrasandeep <sa...@oracle.com>
AuthorDate: Thu May 13 10:40:12 2021 +0530

    Added Check regular expression window and hint. Modified Malformed Regexp Hint.
---
 java/java.hints/licenseinfo.xml                    |   3 +
 java/java.hints/nbproject/project.xml              |   9 +
 .../modules/java/hints/bugs/Bundle.properties      |   8 -
 .../netbeans/modules/java/hints/bugs/Regexp.java   | 172 -----------
 .../modules/java/hints/jdk/Bundle.properties       |   9 +
 .../modules/java/hints/jdk/CheckRegex.java         | 235 ++++++++++++++
 .../java/hints/jdk/CheckRegexTopComponent.form     | 198 ++++++++++++
 .../java/hints/jdk/CheckRegexTopComponent.java     | 341 +++++++++++++++++++++
 .../netbeans/modules/java/hints/jdk/Regexp.java    | 185 +++++++++++
 .../modules/java/hints/resources/half-match.png    | Bin 0 -> 654 bytes
 .../modules/java/hints/resources/match.png         | Bin 0 -> 495 bytes
 .../modules/java/hints/resources/no-match.png      | Bin 0 -> 339 bytes
 .../modules/java/hints/jdk/CheckRegexTest.java     |  54 ++++
 .../java/hints/{bugs => jdk}/RegexpTest.java       |   6 +-
 14 files changed, 1037 insertions(+), 183 deletions(-)

diff --git a/java/java.hints/licenseinfo.xml b/java/java.hints/licenseinfo.xml
index f12e06e..5f02fc5 100644
--- a/java/java.hints/licenseinfo.xml
+++ b/java/java.hints/licenseinfo.xml
@@ -27,6 +27,9 @@
         <file>src/org/netbeans/modules/java/hints/analyzer/ui/warning-glyph.gif</file>
         <file>src/org/netbeans/modules/java/hints/analyzer/ui/suggestion.png</file>
         <file>src/org/netbeans/modules/java/hints/analyzer/ui/refactoringpreview.png</file>
+        <file>src/org/netbeans/modules/java/hints/resources/match.png</file>
+        <file>src/org/netbeans/modules/java/hints/resources/half-match.png</file>
+        <file>src/org/netbeans/modules/java/hints/resources/no-match.png</file>
         <license ref="Apache-2.0-ASF" />
         <comment type="COMMENT_UNSUPPORTED" />
     </fileset>
diff --git a/java/java.hints/nbproject/project.xml b/java/java.hints/nbproject/project.xml
index a94078f..ce13a87 100644
--- a/java/java.hints/nbproject/project.xml
+++ b/java/java.hints/nbproject/project.xml
@@ -358,6 +358,15 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
+                    <code-name-base>org.netbeans.modules.settings</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.61</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     <code-name-base>org.netbeans.spi.editor.hints</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Bundle.properties b/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Bundle.properties
index e225ec7..aca26e7 100644
--- a/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Bundle.properties
+++ b/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Bundle.properties
@@ -21,18 +21,10 @@ DN_AnnotationsNotRuntime_isAnnotation={0} does not have runtime Retention, the r
 DN_AnnotationsNotRuntime_getAnnotation={0} does not have runtime Retention, the result will always be null
 # {0} - the name of the annotation
 DN_AnnotationsNotRuntime_instanceof={0} does not have runtime Retention, the condition will always be false
-#{0}: PatternSyntaxException.getDescription()
-#{1}: PatternSyntaxException.getMessage()
-#{2}: PatternSyntaxException.getPattern()
-#{3}: PatternSyntaxException.getIndex()
-DN_RegExp=Invalid regular expression: {0}
 
 DN_org.netbeans.modules.java.hints.bugs.AnnotationsNotRuntime=Annotations without runtime Retention
 DESC_org.netbeans.modules.java.hints.bugs.AnnotationsNotRuntime=Warns about reflective access to annotations with CLASS or SOURCE retentions
 
-DN_org.netbeans.modules.java.hints.bugs.Regexp=Malformed regular expression
-DESC_org.netbeans.modules.java.hints.bugs.Regexp=Warns about malformed regular expressions
-
 DN_org.netbeans.modules.java.hints.bugs.Tiny.stringReplaceAllDot=String.replaceAll(".", )
 DESC_org.netbeans.modules.java.hints.bugs.Tiny.stringReplaceAllDot=Finds occurrences of calls to String.replaceAll(".", $target), which would replace all characters of the source string with $target.
 ERR_string-replace-all-dot=Call to String.replaceAll(".", $target) is probably undesired
diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Regexp.java b/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Regexp.java
deleted file mode 100644
index 4be2cd9..0000000
--- a/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Regexp.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.netbeans.modules.java.hints.bugs;
-
-import com.sun.source.tree.BinaryTree;
-import com.sun.source.tree.IdentifierTree;
-import com.sun.source.tree.LiteralTree;
-import com.sun.source.tree.MemberSelectTree;
-import com.sun.source.tree.MethodInvocationTree;
-import com.sun.source.tree.Tree.Kind;
-import com.sun.source.util.TreePath;
-import org.netbeans.api.java.source.support.ErrorAwareTreePathScanner;
-import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.VariableElement;
-import org.netbeans.spi.java.hints.ConstraintVariableType;
-import org.netbeans.spi.java.hints.Hint;
-import org.netbeans.spi.java.hints.TriggerPattern;
-import org.netbeans.spi.java.hints.TriggerPatterns;
-import org.netbeans.spi.java.hints.HintContext;
-import org.netbeans.spi.java.hints.ErrorDescriptionFactory;
-import org.netbeans.spi.editor.hints.ErrorDescription;
-import org.netbeans.spi.java.hints.Hint.Options;
-import org.openide.util.NbBundle;
-
-/**
- *
- * @author lahvac
- */
-@Hint(displayName = "#DN_org.netbeans.modules.java.hints.bugs.Regexp", description = "#DESC_org.netbeans.modules.java.hints.bugs.Regexp", category="bugs", suppressWarnings={"MalformedRegexp", "", "MalformedRegex"}, options=Options.QUERY)
-public class Regexp {
-
-    @TriggerPatterns({
-        @TriggerPattern(value="java.util.regex.Pattern.compile($pattern)",
-                        constraints={
-                            @ConstraintVariableType(variable="$pattern", type="java.lang.String")
-                        }),
-        @TriggerPattern(value="java.util.regex.Pattern.compile($pattern, $flags)",
-                        constraints={
-                            @ConstraintVariableType(variable="$pattern", type="java.lang.String"),
-                            @ConstraintVariableType(variable="$flags", type="int")
-                        }),
-        @TriggerPattern(value="java.util.regex.Pattern.matches($pattern, $text)",
-                        constraints={
-                            @ConstraintVariableType(variable="$pattern", type="java.lang.String"),
-                            @ConstraintVariableType(variable="$text", type="java.lang.CharSequence")
-                        }),
-        @TriggerPattern(value="$str.split($pattern)",
-                        constraints={
-                            @ConstraintVariableType(variable="$str", type="java.lang.String"),
-                            @ConstraintVariableType(variable="$pattern", type="java.lang.String")
-                        }),
-        @TriggerPattern(value="$str.split($pattern, $limit)",
-                        constraints={
-                            @ConstraintVariableType(variable="$str", type="java.lang.String"),
-                            @ConstraintVariableType(variable="$pattern", type="java.lang.String"),
-                            @ConstraintVariableType(variable="$limit", type="int")
-                        }),
-        @TriggerPattern(value="$str.matches($pattern)",
-                        constraints={
-                            @ConstraintVariableType(variable="$str", type="java.lang.String"),
-                            @ConstraintVariableType(variable="$pattern", type="java.lang.String")
-                        }),
-        @TriggerPattern(value="$str.replaceFirst($pattern, $repl)",
-                        constraints={
-                            @ConstraintVariableType(variable="$str", type="java.lang.String"),
-                            @ConstraintVariableType(variable="$pattern", type="java.lang.String"),
-                            @ConstraintVariableType(variable="$repl", type="java.lang.String")
-                        }),
-        @TriggerPattern(value="$str.replaceAll($pattern, $repl)",
-                        constraints={
-                            @ConstraintVariableType(variable="$str", type="java.lang.String"),
-                            @ConstraintVariableType(variable="$pattern", type="java.lang.String"),
-                            @ConstraintVariableType(variable="$repl", type="java.lang.String")
-                        })
-    })
-    public static ErrorDescription hint(final HintContext ctx) {
-        final StringBuilder regexp = new StringBuilder();
-        final boolean[] accept = {true};
-        TreePath pattern = ctx.getVariables().get("$pattern");
-        new ErrorAwareTreePathScanner<Void, Void>() {
-            @Override
-            public Void visitLiteral(LiteralTree node, Void p) {
-                if (node.getValue() instanceof String) {
-                    regexp.append(node.getValue());
-                    return null;
-                }
-                accept[0] = false;
-                return null;
-            }
-            @Override
-            public Void visitIdentifier(IdentifierTree node, Void p) {
-                Element el = ctx.getInfo().getTrees().getElement(getCurrentPath());
-
-                if (el != null && el.getKind() == ElementKind.FIELD) {
-                    VariableElement ve = (VariableElement) el;
-
-                    if (ve.getConstantValue() instanceof String) {
-                        regexp.append(ve.getConstantValue());
-                        return null;
-                    }
-                }
-                accept[0] = false;
-                return null;
-            }
-            @Override
-            public Void visitMemberSelect(MemberSelectTree node, Void p) {
-                Element el = ctx.getInfo().getTrees().getElement(getCurrentPath());
-
-                if (el != null && el.getKind() == ElementKind.FIELD) {
-                    VariableElement ve = (VariableElement) el;
-
-                    if (ve.getConstantValue() instanceof String) {
-                        regexp.append(ve.getConstantValue());
-                        return null;
-                    }
-                }
-                accept[0] = false;
-                return null;
-            }
-            @Override
-            public Void visitBinary(BinaryTree node, Void p) {
-                if (node.getKind() != Kind.PLUS) {
-                    return super.visitBinary(node, p);
-                }
-                accept[0] = false;
-                return null;
-            }
-            @Override
-            public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
-                accept[0] = false;
-                return null;
-            }
-        }.scan(pattern, null);
-
-        if (!accept[0] || regexp.length() == 0) {
-            return null;
-        }
-
-        try {
-            Pattern.compile(regexp.toString());
-            return null;
-        } catch (PatternSyntaxException pse) {
-            String displayName = NbBundle.getMessage(Regexp.class, "DN_RegExp", new Object[] {
-                pse.getDescription(),
-                pse.getMessage(),
-                pse.getPattern(),
-                pse.getIndex(),
-            });
-            return ErrorDescriptionFactory.forTree(ctx, pattern, displayName);
-        }
-    }
-}
diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/jdk/Bundle.properties b/java/java.hints/src/org/netbeans/modules/java/hints/jdk/Bundle.properties
index d595271..2298007 100644
--- a/java/java.hints/src/org/netbeans/modules/java/hints/jdk/Bundle.properties
+++ b/java/java.hints/src/org/netbeans/modules/java/hints/jdk/Bundle.properties
@@ -111,3 +111,12 @@ DESC_AnnoProcessor_ObsoleteSupportedSource=If the project's source level is grea
     another useless warning during compilation. <p/>\
     It is recommended to return at least the current project's source level. \
     For future compatibility, consider to return <b>SourceVersion.latest()</b>; most Processors are not affected by future language changes.
+CheckRegexTopComponent.regexLabel.text=Re&gular Expression:
+CheckRegexTopComponent.exampleLabel.text=E&xample:
+CheckRegexTopComponent.strictCheckBox.text=Str&ict Check
+
+#{0}: PatternSyntaxException.getDescription()
+#{1}: PatternSyntaxException.getMessage()
+#{2}: PatternSyntaxException.getPattern()
+#{3}: PatternSyntaxException.getIndex()
+DN_RegExp=Invalid regular expression: {0}
diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/jdk/CheckRegex.java b/java/java.hints/src/org/netbeans/modules/java/hints/jdk/CheckRegex.java
new file mode 100644
index 0000000..5d6179d
--- /dev/null
+++ b/java/java.hints/src/org/netbeans/modules/java/hints/jdk/CheckRegex.java
@@ -0,0 +1,235 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.hints.jdk;
+
+import com.sun.source.tree.AssignmentTree;
+import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.ExpressionStatementTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.LiteralTree;
+import com.sun.source.tree.StatementTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.TreePath;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import java.util.List;
+import javax.lang.model.element.Name;
+import javax.swing.SwingUtilities;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.spi.editor.hints.ErrorDescription;
+import org.netbeans.spi.java.hints.ConstraintVariableType;
+import org.netbeans.spi.java.hints.ErrorDescriptionFactory;
+import org.netbeans.spi.java.hints.Hint;
+import org.netbeans.spi.java.hints.HintContext;
+import org.netbeans.spi.java.hints.JavaFix;
+import org.netbeans.spi.java.hints.TriggerPattern;
+import org.netbeans.spi.java.hints.TriggerPatterns;
+import org.openide.util.NbBundle.Messages;
+
+@Hint(displayName = "#DN_CheckRegex", description = "#DESC_CheckRegex", category = "general")
+@Messages({
+    "DN_CheckRegex=Check Regular Expression",
+    "DESC_CheckRegex=Check Regular Expression"
+})
+public class CheckRegex {
+
+    @TriggerPatterns({
+        @TriggerPattern(value = "java.util.regex.Pattern.compile($pattern)",
+                constraints = {
+                    @ConstraintVariableType(variable = "$pattern", type = "java.lang.String")
+                }),
+        @TriggerPattern(value = "java.util.regex.Pattern.compile($pattern, $flags)",
+                constraints = {
+                    @ConstraintVariableType(variable = "$pattern", type = "java.lang.String"),
+                    @ConstraintVariableType(variable = "$flags", type = "int")
+                }),
+        @TriggerPattern(value = "java.util.regex.Pattern.matches($pattern, $text)",
+                constraints = {
+                    @ConstraintVariableType(variable = "$pattern", type = "java.lang.String"),
+                    @ConstraintVariableType(variable = "$text", type = "java.lang.CharSequence")
+                }),
+        @TriggerPattern(value = "$str.split($pattern)",
+                constraints = {
+                    @ConstraintVariableType(variable = "$str", type = "java.lang.String"),
+                    @ConstraintVariableType(variable = "$pattern", type = "java.lang.String")
+                }),
+        @TriggerPattern(value = "$str.split($pattern, $limit)",
+                constraints = {
+                    @ConstraintVariableType(variable = "$str", type = "java.lang.String"),
+                    @ConstraintVariableType(variable = "$pattern", type = "java.lang.String"),
+                    @ConstraintVariableType(variable = "$limit", type = "int")
+                }),
+        @TriggerPattern(value = "$str.matches($pattern)",
+                constraints = {
+                    @ConstraintVariableType(variable = "$str", type = "java.lang.String"),
+                    @ConstraintVariableType(variable = "$pattern", type = "java.lang.String")
+                }),
+        @TriggerPattern(value = "$str.replaceFirst($pattern, $repl)",
+                constraints = {
+                    @ConstraintVariableType(variable = "$str", type = "java.lang.String"),
+                    @ConstraintVariableType(variable = "$pattern", type = "java.lang.String"),
+                    @ConstraintVariableType(variable = "$repl", type = "java.lang.String")
+                }),
+        @TriggerPattern(value = "$str.replaceAll($pattern, $repl)",
+                constraints = {
+                    @ConstraintVariableType(variable = "$str", type = "java.lang.String"),
+                    @ConstraintVariableType(variable = "$pattern", type = "java.lang.String"),
+                    @ConstraintVariableType(variable = "$repl", type = "java.lang.String")
+                })
+    })
+    @CheckForNull
+    @Messages({"ERR_CheckRegex=Check regular expression",
+        "# {0} - invalidRegex",
+        "ERR_InvalidRegex=Invalid regular expression: {0}"})
+    public static ErrorDescription computeWarning(HintContext ctx) {
+
+        String originalString = null;
+        Tree leaf = ctx.getVariables().get("$pattern").getLeaf();  // NOI18N
+
+        if (leaf.getKind() == Kind.STRING_LITERAL) {
+            originalString = (String) ((LiteralTree) leaf).getValue();
+        } else if (leaf.getKind() == Kind.IDENTIFIER) {
+            originalString = identifierSearch(leaf, ctx);
+        }
+
+        return ErrorDescriptionFactory.forName(ctx, ctx.getPath(), Bundle.ERR_CheckRegex(), new FixImpl(ctx.getInfo(), ctx.getPath(), originalString).toEditorFix());
+    }
+
+    @CheckForNull
+    public static String identifierSearch(Tree leaf, HintContext ctx) {
+        String originalString = null;
+        Name name = ((IdentifierTree) leaf).getName();
+        TreePath tp = ctx.getPath().getParentPath();
+
+        Tree statement = tp.getLeaf();
+        BlockTree bt = null;
+        while (originalString == null && tp != null) {
+            if (tp.getLeaf() instanceof JCBlock) {
+                originalString = identifierBlockSearch(tp.getLeaf(), name, statement, bt);
+                bt = (BlockTree) tp.getLeaf();
+            } else if (tp.getLeaf() instanceof JCClassDecl) {
+                originalString = identifierClassSearch(tp.getLeaf(), name);
+            }
+            tp = tp.getParentPath();
+        }
+        return originalString;
+    }
+
+    @CheckForNull
+    private static String identifierBlockSearch(Tree leaf, Name name, Tree statement, BlockTree blocktree) {
+        String res = null;
+        try {
+            BlockTree bt = (BlockTree) leaf;
+            List<? extends StatementTree> statements = bt.getStatements();
+            for (int i = 0; i < statements.size(); i++) {
+                StatementTree st = statements.get(i);
+                if (st.equals(statement)) {
+                    return res;
+                }
+                if (st.equals(blocktree)) {
+                    return res;
+                }
+                if (st instanceof VariableTree) {
+                    VariableTree vt = (VariableTree) st;
+                    if (vt.getType().toString().equalsIgnoreCase("String") && vt.getName().equals(name)) {  // NOI18N
+                        LiteralTree lt = (LiteralTree) vt.getInitializer();
+                        res = (String) lt.getValue();
+                    }
+                } else if (st instanceof ExpressionStatementTree) {
+                    ExpressionStatementTree est = (ExpressionStatementTree) st;
+                    ExpressionTree et = est.getExpression();
+                    if (et instanceof AssignmentTree) {
+                        AssignmentTree at = (AssignmentTree) et;
+                        if (at.getVariable().toString().equals(name.toString())) {
+                            res = (String) ((LiteralTree) ((AssignmentTree) est.getExpression()).getExpression()).getValue();
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            return null;
+        }
+        return res;
+    }
+
+    @CheckForNull
+    private static String identifierClassSearch(Tree leaf, Name name) {
+        String res = null;
+        try {
+            ClassTree ct = (ClassTree) leaf;
+            List<? extends Tree> members = ct.getMembers();
+            for (int i = 0; i < members.size(); i++) {
+                Tree t = members.get(i);
+                if (t instanceof VariableTree) {
+                    VariableTree vt = (VariableTree) t;
+                    if (vt.getType().toString().equalsIgnoreCase("String") && vt.getName().equals(name)) {  // NOI18N
+                        LiteralTree lt = (LiteralTree) vt.getInitializer();
+                        res = lt != null ? (String) lt.getValue() : null;
+                    }
+                } else if (t instanceof ExpressionStatementTree) {
+                    ExpressionStatementTree est = (ExpressionStatementTree) t;
+                    ExpressionTree et = est.getExpression();
+                    if (et instanceof AssignmentTree) {
+                        AssignmentTree at = (AssignmentTree) et;
+                        if (at.getVariable().toString().equals(name.toString())) {
+                            res = (String) ((LiteralTree) ((AssignmentTree) est.getExpression()).getExpression()).getValue();
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            return null;
+        }
+        return res;
+    }
+
+    private static final class FixImpl extends JavaFix {
+
+        private final String origString;
+
+        private FixImpl(CompilationInfo info, TreePath path, String origString) {
+            super(info, path);
+            this.origString = origString;
+        }
+
+        @Override
+        protected String getText() {
+            return Bundle.DESC_CheckRegex();
+        }
+
+        @Override
+        protected void performRewrite(TransformationContext tc) throws Exception {
+            SwingUtilities.invokeLater(new Runnable() {
+                @Override
+                public void run() {
+                    CheckRegexTopComponent win = CheckRegexTopComponent.findInstance();
+                    win.open();
+                    win.requestActive();
+                    win.setData(origString);
+                }
+            });
+        }
+
+    }
+}
diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/jdk/CheckRegexTopComponent.form b/java/java.hints/src/org/netbeans/modules/java/hints/jdk/CheckRegexTopComponent.form
new file mode 100644
index 0000000..56e5790
--- /dev/null
+++ b/java/java.hints/src/org/netbeans/modules/java/hints/jdk/CheckRegexTopComponent.form
@@ -0,0 +1,198 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+
+<Form version="1.9" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Component id="regexLabel" alignment="0" min="-2" max="-2" attributes="0"/>
+                  <Component id="exampleLabel" alignment="0" min="-2" max="-2" attributes="0"/>
+                  <Group type="102" alignment="0" attributes="0">
+                      <Component id="regexScrollPane" min="-2" pref="320" max="-2" attributes="0"/>
+                      <EmptySpace type="unrelated" max="-2" attributes="0"/>
+                      <Component id="errorLabel" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <Component id="exampleLayeredPane" alignment="0" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace pref="266" max="32767" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="1" attributes="0">
+              <EmptySpace max="32767" attributes="0"/>
+              <Component id="regexLabel" min="-2" max="-2" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Component id="regexScrollPane" min="-2" pref="42" max="-2" attributes="0"/>
+                  <Component id="errorLabel" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace type="unrelated" max="-2" attributes="0"/>
+              <Component id="exampleLabel" min="-2" max="-2" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="exampleLayeredPane" min="-2" max="-2" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Component class="javax.swing.JLabel" name="regexLabel">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/java/hints/jdk/Bundle.properties" key="CheckRegexTopComponent.regexLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Container class="javax.swing.JScrollPane" name="regexScrollPane">
+      <Properties>
+        <Property name="verticalScrollBarPolicy" type="int" value="21"/>
+        <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[164, 74]"/>
+        </Property>
+      </Properties>
+      <AuxValues>
+        <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+      </AuxValues>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JTextArea" name="regexTextArea">
+          <Properties>
+            <Property name="columns" type="int" value="20"/>
+            <Property name="rows" type="int" value="5"/>
+            <Property name="focusAccelerator" type="char" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+              <Connection code="&apos;g&apos;" type="code"/>
+            </Property>
+          </Properties>
+          <Events>
+            <EventHandler event="keyReleased" listener="java.awt.event.KeyListener" parameters="java.awt.event.KeyEvent" handler="regexTextAreaKeyReleased"/>
+          </Events>
+        </Component>
+      </SubComponents>
+    </Container>
+    <Component class="javax.swing.JLabel" name="exampleLabel">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/java/hints/jdk/Bundle.properties" key="CheckRegexTopComponent.exampleLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Container class="javax.swing.JLayeredPane" name="exampleLayeredPane">
+
+      <Layout>
+        <DimensionLayout dim="0">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="1" attributes="0">
+                  <EmptySpace pref="297" max="32767" attributes="0"/>
+                  <Component id="iconLabel" min="-2" pref="17" max="-2" attributes="0"/>
+                  <EmptySpace min="-2" pref="12" max="-2" attributes="0"/>
+                  <Component id="strictCheckBox" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
+                  <Group type="102" alignment="0" attributes="0">
+                      <Component id="exampleScrollPane" min="-2" pref="320" max="-2" attributes="0"/>
+                      <EmptySpace min="0" pref="15" max="32767" attributes="0"/>
+                  </Group>
+              </Group>
+          </Group>
+        </DimensionLayout>
+        <DimensionLayout dim="1">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="0" attributes="0">
+                  <EmptySpace min="-2" pref="21" max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <Component id="strictCheckBox" min="-2" max="-2" attributes="0"/>
+                      <Component id="iconLabel" min="-2" pref="18" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace max="32767" attributes="0"/>
+              </Group>
+              <Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
+                  <Group type="102" alignment="0" attributes="0">
+                      <Component id="exampleScrollPane" min="-2" pref="44" max="-2" attributes="0"/>
+                      <EmptySpace min="0" pref="6" max="32767" attributes="0"/>
+                  </Group>
+              </Group>
+          </Group>
+        </DimensionLayout>
+      </Layout>
+      <SubComponents>
+        <Component class="javax.swing.JLabel" name="iconLabel">
+        </Component>
+        <Container class="javax.swing.JScrollPane" name="exampleScrollPane">
+          <Properties>
+            <Property name="verticalScrollBarPolicy" type="int" value="21"/>
+            <Property name="opaque" type="boolean" value="false"/>
+          </Properties>
+          <AuxValues>
+            <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+          </AuxValues>
+
+          <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+          <SubComponents>
+            <Component class="javax.swing.JTextArea" name="exampleTextArea">
+              <Properties>
+                <Property name="columns" type="int" value="20"/>
+                <Property name="rows" type="int" value="5"/>
+                <Property name="focusAccelerator" type="char" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+                  <Connection code="&apos;x&apos;" type="code"/>
+                </Property>
+              </Properties>
+              <Events>
+                <EventHandler event="keyReleased" listener="java.awt.event.KeyListener" parameters="java.awt.event.KeyEvent" handler="exampleTextAreaKeyReleased"/>
+              </Events>
+            </Component>
+          </SubComponents>
+        </Container>
+        <Component class="javax.swing.JCheckBox" name="strictCheckBox">
+          <Properties>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="org/netbeans/modules/java/hints/jdk/Bundle.properties" key="CheckRegexTopComponent.strictCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+          </Properties>
+          <Events>
+            <EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="strictCheckBoxItemStateChanged"/>
+          </Events>
+        </Component>
+      </SubComponents>
+    </Container>
+    <Component class="javax.swing.JLabel" name="errorLabel">
+    </Component>
+  </SubComponents>
+</Form>
diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/jdk/CheckRegexTopComponent.java b/java/java.hints/src/org/netbeans/modules/java/hints/jdk/CheckRegexTopComponent.java
new file mode 100644
index 0000000..98652b0
--- /dev/null
+++ b/java/java.hints/src/org/netbeans/modules/java/hints/jdk/CheckRegexTopComponent.java
@@ -0,0 +1,341 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.hints.jdk;
+
+import java.awt.Color;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.DefaultHighlighter;
+import javax.swing.text.Highlighter;
+import org.netbeans.api.settings.ConvertAsProperties;
+import org.openide.awt.ActionID;
+import org.openide.awt.ActionReference;
+import org.openide.awt.ActionReferences;
+import org.openide.util.Exceptions;
+import org.openide.util.NbBundle;
+import org.openide.windows.TopComponent;
+import org.openide.util.NbBundle.Messages;
+import org.openide.windows.Mode;
+import org.openide.windows.WindowManager;
+
+/**
+ * Top component which displays something.
+ */
+@ConvertAsProperties(
+        dtd = "-//org.netbeans.modules.java.hints.jdk//CheckRegex//EN",
+        autostore = false
+)
+@TopComponent.Description(
+        preferredID = "CheckRegexTopComponent",
+        persistenceType = TopComponent.PERSISTENCE_ALWAYS
+)
+@TopComponent.Registration(mode = "output", openAtStartup = false, position = 13000)
+@ActionID(category = "Window", id = "org.netbeans.modules.java.hints.jdk.CheckRegexTopComponent")
+@ActionReferences({
+    @ActionReference(name = "Check Regex", path = "Menu/Window", position = 950),
+    @ActionReference(path = "Shortcuts", name = "C-8")})
+@TopComponent.OpenActionRegistration(
+        displayName = "#CTL_CheckRegexAction",
+        preferredID = "CheckRegexTopComponent"
+)
+@Messages({
+    "CTL_CheckRegexAction=Check Regex",
+    "CTL_CheckRegexTopComponent=Check Regular Expression",
+    "HINT_CheckRegexTopComponent=This is a Check Regex window"
+})
+public final class CheckRegexTopComponent extends TopComponent {
+
+    private static CheckRegexTopComponent instance;
+    private static final String PREFERRED_ID = "CheckRegexTopComponent";
+    private static boolean isStrictMatch;
+
+    public CheckRegexTopComponent() {
+        initComponents();
+        setName(Bundle.CTL_CheckRegexTopComponent());
+        setToolTipText(Bundle.HINT_CheckRegexTopComponent());
+        isStrictMatch = false;
+    }
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is always
+     * regenerated by the Form Editor.
+     */
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        regexLabel = new javax.swing.JLabel();
+        regexScrollPane = new javax.swing.JScrollPane();
+        regexTextArea = new javax.swing.JTextArea();
+        exampleLabel = new javax.swing.JLabel();
+        exampleLayeredPane = new javax.swing.JLayeredPane();
+        iconLabel = new javax.swing.JLabel();
+        exampleScrollPane = new javax.swing.JScrollPane();
+        exampleTextArea = new javax.swing.JTextArea();
+        strictCheckBox = new javax.swing.JCheckBox();
+        errorLabel = new javax.swing.JLabel();
+
+        org.openide.awt.Mnemonics.setLocalizedText(regexLabel, org.openide.util.NbBundle.getMessage(CheckRegexTopComponent.class, "CheckRegexTopComponent.regexLabel.text")); // NOI18N
+
+        regexScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
+        regexScrollPane.setPreferredSize(new java.awt.Dimension(164, 74));
+
+        regexTextArea.setColumns(20);
+        regexTextArea.setRows(5);
+        regexTextArea.setFocusAccelerator('g');
+        regexTextArea.addKeyListener(new java.awt.event.KeyAdapter() {
+            public void keyReleased(java.awt.event.KeyEvent evt) {
+                regexTextAreaKeyReleased(evt);
+            }
+        });
+        regexScrollPane.setViewportView(regexTextArea);
+
+        org.openide.awt.Mnemonics.setLocalizedText(exampleLabel, org.openide.util.NbBundle.getMessage(CheckRegexTopComponent.class, "CheckRegexTopComponent.exampleLabel.text")); // NOI18N
+
+        exampleScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
+        exampleScrollPane.setOpaque(false);
+
+        exampleTextArea.setColumns(20);
+        exampleTextArea.setRows(5);
+        exampleTextArea.setFocusAccelerator('x');
+        exampleTextArea.addKeyListener(new java.awt.event.KeyAdapter() {
+            public void keyReleased(java.awt.event.KeyEvent evt) {
+                exampleTextAreaKeyReleased(evt);
+            }
+        });
+        exampleScrollPane.setViewportView(exampleTextArea);
+
+        org.openide.awt.Mnemonics.setLocalizedText(strictCheckBox, org.openide.util.NbBundle.getMessage(CheckRegexTopComponent.class, "CheckRegexTopComponent.strictCheckBox.text")); // NOI18N
+        strictCheckBox.addItemListener(new java.awt.event.ItemListener() {
+            public void itemStateChanged(java.awt.event.ItemEvent evt) {
+                strictCheckBoxItemStateChanged(evt);
+            }
+        });
+
+        exampleLayeredPane.setLayer(iconLabel, javax.swing.JLayeredPane.DEFAULT_LAYER);
+        exampleLayeredPane.setLayer(exampleScrollPane, javax.swing.JLayeredPane.DEFAULT_LAYER);
+        exampleLayeredPane.setLayer(strictCheckBox, javax.swing.JLayeredPane.DEFAULT_LAYER);
+
+        javax.swing.GroupLayout exampleLayeredPaneLayout = new javax.swing.GroupLayout(exampleLayeredPane);
+        exampleLayeredPane.setLayout(exampleLayeredPaneLayout);
+        exampleLayeredPaneLayout.setHorizontalGroup(
+            exampleLayeredPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, exampleLayeredPaneLayout.createSequentialGroup()
+                .addContainerGap(297, Short.MAX_VALUE)
+                .addComponent(iconLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 17, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addGap(12, 12, 12)
+                .addComponent(strictCheckBox))
+            .addGroup(exampleLayeredPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                .addGroup(exampleLayeredPaneLayout.createSequentialGroup()
+                    .addComponent(exampleScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 320, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addGap(0, 15, Short.MAX_VALUE)))
+        );
+        exampleLayeredPaneLayout.setVerticalGroup(
+            exampleLayeredPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(exampleLayeredPaneLayout.createSequentialGroup()
+                .addGap(21, 21, 21)
+                .addGroup(exampleLayeredPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addComponent(strictCheckBox)
+                    .addComponent(iconLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE))
+                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+            .addGroup(exampleLayeredPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                .addGroup(exampleLayeredPaneLayout.createSequentialGroup()
+                    .addComponent(exampleScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 44, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addGap(0, 6, Short.MAX_VALUE)))
+        );
+
+        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+        this.setLayout(layout);
+        layout.setHorizontalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addComponent(regexLabel)
+                    .addComponent(exampleLabel)
+                    .addGroup(layout.createSequentialGroup()
+                        .addComponent(regexScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 320, javax.swing.GroupLayout.PREFERRED_SIZE)
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+                        .addComponent(errorLabel))
+                    .addComponent(exampleLayeredPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                .addContainerGap(266, Short.MAX_VALUE))
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                .addComponent(regexLabel)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addComponent(regexScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 42, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(errorLabel))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+                .addComponent(exampleLabel)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(exampleLayeredPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addContainerGap())
+        );
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void exampleTextAreaKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_exampleTextAreaKeyReleased
+        if (exampleTextArea.getSelectedText() == null) {
+            matchPattern();
+        }
+    }//GEN-LAST:event_exampleTextAreaKeyReleased
+
+    private void regexTextAreaKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_regexTextAreaKeyReleased
+        matchPattern();
+    }//GEN-LAST:event_regexTextAreaKeyReleased
+
+    private void strictCheckBoxItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_strictCheckBoxItemStateChanged
+        isStrictMatch = !isStrictMatch;
+        matchPattern();
+    }//GEN-LAST:event_strictCheckBoxItemStateChanged
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JLabel errorLabel;
+    private javax.swing.JLabel exampleLabel;
+    private javax.swing.JLayeredPane exampleLayeredPane;
+    private javax.swing.JScrollPane exampleScrollPane;
+    private javax.swing.JTextArea exampleTextArea;
+    private javax.swing.JLabel iconLabel;
+    private javax.swing.JLabel regexLabel;
+    private javax.swing.JScrollPane regexScrollPane;
+    private javax.swing.JTextArea regexTextArea;
+    private javax.swing.JCheckBox strictCheckBox;
+    // End of variables declaration//GEN-END:variables
+    @Override
+    public void componentOpened() {
+        // TODO add custom code on component opening
+    }
+
+    @Override
+    public void componentClosed() {
+        // TODO add custom code on component closing
+    }
+
+    void writeProperties(java.util.Properties p) {
+        // better to version settings since initial version as advocated at
+        // http://wiki.apidesign.org/wiki/PropertyFiles
+        p.setProperty("version", "1.0");    // NOI18N
+        // TODO store your settings
+    }
+
+    void readProperties(java.util.Properties p) {
+        String version = p.getProperty("version");  // NOI18N
+        // TODO read your settings according to their version
+    }
+
+    public static synchronized CheckRegexTopComponent getDefault() {
+        if (instance == null) {
+            instance = new CheckRegexTopComponent();
+        }
+        Mode outputMode = WindowManager.getDefault().findMode("output");    // NOI18N
+        if (outputMode != null) {
+            outputMode.dockInto(instance);
+        }
+        return instance;
+    }
+
+    public static synchronized CheckRegexTopComponent findInstance() {
+        TopComponent win = WindowManager.getDefault().findTopComponent(PREFERRED_ID);
+        if (win == null) {
+            Logger.getLogger(CheckRegexTopComponent.class.getName()).warning(
+                    "Cannot find " + PREFERRED_ID + " component. It will not be located properly in the window system.");   // NOI18N
+            return getDefault();
+        }
+        if (win instanceof CheckRegexTopComponent) {
+            return (CheckRegexTopComponent) win;
+        }
+        Logger.getLogger(CheckRegexTopComponent.class.getName()).warning(
+                "There seem to be multiple components with the '" + PREFERRED_ID
+                + "' ID. That is a potential source of errors and unexpected behavior.");   // NOI18N
+        return getDefault();
+    }
+
+    void setData(String origString) {
+        regexTextArea.setText(origString);
+        matchPattern();
+    }
+
+    @NbBundle.Messages({
+        "CheckRegexTopComponent.tooltip.match.regex=The Example Matches the Regular Expression",
+        "CheckRegexTopComponent.tooltip.need.more.input=Need more input to match",
+        "CheckRegexTopComponent.tooltip.not.match=The example does not match the Regular Expression.",
+        "# {0} - matchCount",
+        "CheckRegexTopComponent.tooltop.sub.match={0} substring(s) match the Regular Expression",
+        "# {0} - invalidRegex",
+        "CheckRegexTopComponent.label.error=Invalid regular expression: {0}"
+    })
+    private void matchPattern() {
+
+        Highlighter highlighter = exampleTextArea.getHighlighter();
+        highlighter.removeAllHighlights();
+
+        errorLabel.setText("");
+        iconLabel.setIcon(null);
+
+        if (regexTextArea.getText().length() == 0 || exampleTextArea.getText().length() == 0) {
+            return;
+        }
+        Pattern p;
+        try {
+            p = Pattern.compile(regexTextArea.getText());
+        } catch (PatternSyntaxException pse) {
+            errorLabel.setText(Bundle.CheckRegexTopComponent_label_error(pse.getDescription()));
+            return;
+        }
+        Matcher m = p.matcher(exampleTextArea.getText());
+
+        if (m.matches()) {
+            iconLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/netbeans/modules/java/hints/resources/match.png"))); // NOI18N
+            exampleTextArea.setToolTipText(Bundle.CheckRegexTopComponent_tooltip_match_regex());
+        } else if (m.hitEnd()) {
+            iconLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/netbeans/modules/java/hints/resources/half-match.png"))); // NOI18N
+            exampleTextArea.setToolTipText(Bundle.CheckRegexTopComponent_tooltip_need_more_input());
+        } else if (isStrictMatch) {
+            iconLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/netbeans/modules/java/hints/resources/no-match.png"))); // NOI18N
+            exampleTextArea.setToolTipText(Bundle.CheckRegexTopComponent_tooltip_not_match());
+        } else {
+            m.reset();
+            long count = 0;
+            try {
+                while (m.find()) {
+                    int start = m.start();
+                    int end = m.end();
+                    DefaultHighlighter.DefaultHighlightPainter defaultHighlightPainter = new DefaultHighlighter.DefaultHighlightPainter(Color.GREEN);
+                    highlighter.addHighlight(start, end, defaultHighlightPainter);
+                    count++;
+                }
+                if (count > 0) {
+                    iconLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/netbeans/modules/java/hints/resources/match.png"))); // NOI18N
+                    exampleTextArea.setToolTipText(Bundle.CheckRegexTopComponent_tooltop_sub_match(count));
+                } else {
+                    iconLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/netbeans/modules/java/hints/resources/no-match.png"))); // NOI18N
+                    exampleTextArea.setToolTipText(Bundle.CheckRegexTopComponent_tooltip_not_match());
+                }
+            } catch (BadLocationException e) {
+                Exceptions.printStackTrace(e);
+            }
+        }
+    }
+}
diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/jdk/Regexp.java b/java/java.hints/src/org/netbeans/modules/java/hints/jdk/Regexp.java
new file mode 100644
index 0000000..4810d43
--- /dev/null
+++ b/java/java.hints/src/org/netbeans/modules/java/hints/jdk/Regexp.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.hints.jdk;
+
+import com.sun.source.tree.BinaryTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.LiteralTree;
+import com.sun.source.tree.MemberSelectTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+import com.sun.source.util.TreePath;
+import org.netbeans.api.java.source.support.ErrorAwareTreePathScanner;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.VariableElement;
+import org.netbeans.spi.java.hints.ConstraintVariableType;
+import org.netbeans.spi.java.hints.Hint;
+import org.netbeans.spi.java.hints.TriggerPattern;
+import org.netbeans.spi.java.hints.TriggerPatterns;
+import org.netbeans.spi.java.hints.HintContext;
+import org.netbeans.spi.java.hints.ErrorDescriptionFactory;
+import org.netbeans.spi.editor.hints.ErrorDescription;
+import org.netbeans.spi.editor.hints.Severity;
+import org.netbeans.spi.java.hints.Hint.Options;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author lahvac
+ */
+@Hint(displayName = "#DN_org.netbeans.modules.java.hints.jdk.Regexp", description = "#DESC_org.netbeans.modules.java.hints.jdk.Regexp", category = "bugs", severity = Severity.ERROR, suppressWarnings = {"MalformedRegexp", "", "MalformedRegex"}, options = Options.QUERY)
+@NbBundle.Messages({"DN_org.netbeans.modules.java.hints.jdk.Regexp=Malformed regular expression",
+    "DESC_org.netbeans.modules.java.hints.jdk.Regexp=Warns about malformed regular expressions"})
+public class Regexp {
+
+    @TriggerPatterns({
+        @TriggerPattern(value = "java.util.regex.Pattern.compile($pattern)",
+                constraints = {
+                    @ConstraintVariableType(variable = "$pattern", type = "java.lang.String")
+                }),
+        @TriggerPattern(value = "java.util.regex.Pattern.compile($pattern, $flags)",
+                constraints = {
+                    @ConstraintVariableType(variable = "$pattern", type = "java.lang.String"),
+                    @ConstraintVariableType(variable = "$flags", type = "int")
+                }),
+        @TriggerPattern(value = "java.util.regex.Pattern.matches($pattern, $text)",
+                constraints = {
+                    @ConstraintVariableType(variable = "$pattern", type = "java.lang.String"),
+                    @ConstraintVariableType(variable = "$text", type = "java.lang.CharSequence")
+                }),
+        @TriggerPattern(value = "$str.split($pattern)",
+                constraints = {
+                    @ConstraintVariableType(variable = "$str", type = "java.lang.String"),
+                    @ConstraintVariableType(variable = "$pattern", type = "java.lang.String")
+                }),
+        @TriggerPattern(value = "$str.split($pattern, $limit)",
+                constraints = {
+                    @ConstraintVariableType(variable = "$str", type = "java.lang.String"),
+                    @ConstraintVariableType(variable = "$pattern", type = "java.lang.String"),
+                    @ConstraintVariableType(variable = "$limit", type = "int")
+                }),
+        @TriggerPattern(value = "$str.matches($pattern)",
+                constraints = {
+                    @ConstraintVariableType(variable = "$str", type = "java.lang.String"),
+                    @ConstraintVariableType(variable = "$pattern", type = "java.lang.String")
+                }),
+        @TriggerPattern(value = "$str.replaceFirst($pattern, $repl)",
+                constraints = {
+                    @ConstraintVariableType(variable = "$str", type = "java.lang.String"),
+                    @ConstraintVariableType(variable = "$pattern", type = "java.lang.String"),
+                    @ConstraintVariableType(variable = "$repl", type = "java.lang.String")
+                }),
+        @TriggerPattern(value = "$str.replaceAll($pattern, $repl)",
+                constraints = {
+                    @ConstraintVariableType(variable = "$str", type = "java.lang.String"),
+                    @ConstraintVariableType(variable = "$pattern", type = "java.lang.String"),
+                    @ConstraintVariableType(variable = "$repl", type = "java.lang.String")
+                })
+    })
+    public static ErrorDescription hint(final HintContext ctx) {
+        final StringBuilder regexp = new StringBuilder();
+        final boolean[] accept = {true};
+        Tree leaf = ctx.getVariables().get("$pattern").getLeaf();   // NOI18N
+        if (leaf.getKind() == Kind.STRING_LITERAL) {
+            TreePath pattern = ctx.getVariables().get("$pattern");  // NOI18N
+            new ErrorAwareTreePathScanner<Void, Void>() {
+                @Override
+                public Void visitLiteral(LiteralTree node, Void p) {
+                    if (node.getValue() instanceof String) {
+                        regexp.append(node.getValue());
+                        return null;
+                    }
+                    accept[0] = false;
+                    return null;
+                }
+
+                @Override
+                public Void visitIdentifier(IdentifierTree node, Void p) {
+                    Element el = ctx.getInfo().getTrees().getElement(getCurrentPath());
+
+                    if (el != null && el.getKind() == ElementKind.FIELD) {
+                        VariableElement ve = (VariableElement) el;
+
+                        if (ve.getConstantValue() instanceof String) {
+                            regexp.append(ve.getConstantValue());
+                            return null;
+                        }
+                    }
+                    accept[0] = false;
+                    return null;
+                }
+
+                @Override
+                public Void visitMemberSelect(MemberSelectTree node, Void p) {
+                    Element el = ctx.getInfo().getTrees().getElement(getCurrentPath());
+
+                    if (el != null && el.getKind() == ElementKind.FIELD) {
+                        VariableElement ve = (VariableElement) el;
+
+                        if (ve.getConstantValue() instanceof String) {
+                            regexp.append(ve.getConstantValue());
+                            return null;
+                        }
+                    }
+                    accept[0] = false;
+                    return null;
+                }
+
+                @Override
+                public Void visitBinary(BinaryTree node, Void p) {
+                    if (node.getKind() != Kind.PLUS) {
+                        return super.visitBinary(node, p);
+                    }
+                    accept[0] = false;
+                    return null;
+                }
+
+                @Override
+                public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
+                    accept[0] = false;
+                    return null;
+                }
+            }.scan(pattern, null);
+
+            if (!accept[0] || regexp.length() == 0) {
+                return null;
+            }
+        } else if (leaf.getKind() == Kind.IDENTIFIER) {
+            String val = CheckRegex.identifierSearch(leaf, ctx);
+            if (val != null) {
+                regexp.append(val);
+            }
+        }
+        try {
+            Pattern.compile(regexp.toString());
+            return null;
+        } catch (PatternSyntaxException pse) {
+            String displayName = NbBundle.getMessage(Regexp.class, "DN_RegExp", new Object[]{ // NOI18N
+                pse.getDescription(),
+                pse.getMessage(),
+                pse.getPattern(),
+                pse.getIndex(),});
+            return ErrorDescriptionFactory.forTree(ctx, leaf, displayName);
+        }
+    }
+}
diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/resources/half-match.png b/java/java.hints/src/org/netbeans/modules/java/hints/resources/half-match.png
new file mode 100644
index 0000000..9b0460e
Binary files /dev/null and b/java/java.hints/src/org/netbeans/modules/java/hints/resources/half-match.png differ
diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/resources/match.png b/java/java.hints/src/org/netbeans/modules/java/hints/resources/match.png
new file mode 100644
index 0000000..d37e7ec
Binary files /dev/null and b/java/java.hints/src/org/netbeans/modules/java/hints/resources/match.png differ
diff --git a/java/java.hints/src/org/netbeans/modules/java/hints/resources/no-match.png b/java/java.hints/src/org/netbeans/modules/java/hints/resources/no-match.png
new file mode 100644
index 0000000..44c59b1
Binary files /dev/null and b/java/java.hints/src/org/netbeans/modules/java/hints/resources/no-match.png differ
diff --git a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/CheckRegexTest.java b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/CheckRegexTest.java
new file mode 100644
index 0000000..22eb381
--- /dev/null
+++ b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/CheckRegexTest.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.hints.jdk;
+
+import org.junit.Test;
+import org.netbeans.modules.java.hints.test.api.HintTest;
+
+public class CheckRegexTest {
+
+    @Test
+    public void testWarningProduced() throws Exception {
+        HintTest.create()
+                .input("package test;\n"
+                        + "import java.util.regex.Pattern;\n"
+                        + "public class Test {\n"
+                        + "    public static void main(String[] args) {\n"
+                        + "        Pattern pattern = Pattern.compile(\"a(b|c|d)\");\n"
+                        + "    }\n"
+                        + "}\n")
+                .run(CheckRegex.class)
+                .assertWarnings("4:34-4:41:verifier:" + Bundle.ERR_CheckRegex());
+    }
+    
+    @Test
+    public void testWarningProduced2() throws Exception {
+        HintTest.create()
+                .input("package test;\n"
+                        + "import java.util.regex.Pattern;\n"
+                        + "public class Test {\n"
+                        + "    public static void main(String[] args) {\n"
+                        + "        boolean matches = Pattern.matches(\"\\\\d\", \"abc\");\n"
+                        + "    }\n"
+                        + "}\n")
+                .run(CheckRegex.class)
+                .assertWarnings("4:34-4:41:verifier:" + Bundle.ERR_CheckRegex());
+    }
+    
+}
diff --git a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/bugs/RegexpTest.java b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/RegexpTest.java
similarity index 91%
rename from java/java.hints/test/unit/src/org/netbeans/modules/java/hints/bugs/RegexpTest.java
rename to java/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/RegexpTest.java
index e3f5267..3bb0688 100644
--- a/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/bugs/RegexpTest.java
+++ b/java/java.hints/test/unit/src/org/netbeans/modules/java/hints/jdk/RegexpTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.netbeans.modules.java.hints.bugs;
+package org.netbeans.modules.java.hints.jdk;
 
 import org.netbeans.junit.NbTestCase;
 import org.netbeans.modules.java.hints.test.api.HintTest;
@@ -41,6 +41,6 @@ public class RegexpTest extends NbTestCase {
                        "    }\n" +
                        "}\n")
                 .run(Regexp.class)
-                .assertWarnings("3:40-3:43:verifier:Invalid regular expression");
+                .assertWarnings("3:40-3:43:error:Invalid regular expression: Unclosed group");
     }
-}
\ No newline at end of file
+}

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists