{"action":"created","comment":{"url":"https://api.github.com/repos/checkstyle/checkstyle/pulls/comments/83535801","pull_request_review_id":4375532,"id":83535801,"diff_hunk":"@@ -40,28 +47,129 @@\n     private final PkgControl parent;\n     /** The full package name for the node. */\n     private final String fullPackage;\n+    /**\n+     * The regex pattern for partial match (exact and for subpackages) - only not\n+     * null if regex is true.\n+     */\n+    private final Pattern patternForPartialMatch;\n+    /** The regex pattern for exact matches - only not null if regex is true. */\n+    private final Pattern patternForExactMatch;\n+    /** If this package represents a regular expression. */\n+    private final boolean regex;\n \n     /**\n      * Construct a root node.\n      * @param pkgName the name of the package.\n+     * @param regex flags interpretation of pkgName as regex pattern.\n      */\n-    PkgControl(final String pkgName) {\n+    PkgControl(final String pkgName, final boolean regex) {\n         parent = null;\n+        this.regex = regex;\n         fullPackage = pkgName;\n+        if (regex) {\n+            patternForPartialMatch = createPatternForPartialMatch();\n+            patternForExactMatch = createPatternForExactMatch();\n+        }\n+        else {\n+            patternForPartialMatch = null;\n+            patternForExactMatch = null;\n+        }\n     }\n \n     /**\n-     * Construct a child node.\n+     * Construct a child node. The concatenation of regular expressions needs special care:\n+     * see {@link #ensureSelfContainedRegex(String, boolean)} for more details.\n      * @param parent the parent node.\n      * @param subPkg the sub package name.\n+     * @param regex flags interpretation of subPkg as regex pattern.\n      */\n-    PkgControl(final PkgControl parent, final String subPkg) {\n+    PkgControl(final PkgControl parent, final String subPkg, final boolean regex) {\n         this.parent = parent;\n-        fullPackage = parent.fullPackage + \".\" + subPkg;\n+        if (regex || parent.regex) {\n+            // regex gets inherited\n+            final String parentRegex = ensureSelfContainedRegex(parent.fullPackage, parent.regex);\n+            final String thisRegex = ensureSelfContainedRegex(subPkg, regex);\n+            fullPackage = parentRegex + DOT_REGEX + thisRegex;\n+            patternForPartialMatch = createPatternForPartialMatch();\n+            patternForExactMatch = createPatternForExactMatch();\n+            this.regex = true;\n+        }\n+        else {\n+            fullPackage = parent.fullPackage + DOT + subPkg;\n+            patternForPartialMatch = null;\n+            patternForExactMatch = null;\n+            this.regex = false;\n+        }\n         parent.children.add(this);\n     }\n \n     /**\n+     * Returns a regex that is suitable for concatenation by either converting a plain string into\n+     * a regular expression (handling special characters) or by enclosing {@code input} in a\n+     * (non-capturing) group if {@code input} already is a regular expression.\n+     *\n+     * <p>The grouping is strictly necessary if a) {@code input} is a regular expression that b)\n+     * contains the alteration character ('|') and if c) the pattern is not already enclosed in a\n+     * group - as you see in this example: {@code parent=\"com|org\", child=\"common|uncommon\"} will\n+     * result in the pattern {@code \"(?:org|com)\\.(?common|uncommon)\"} what will match\n+     * {@code \"com.common\"}, {@code \"com.uncommon\"}, {@code \"org.common\"}, and {@code\n+     * \"org.uncommon\"}. Without the grouping it would be {@code \"com|org.common|uncommon\"} which\n+     * would match {@code \"com\"}, {@code \"org.common\"}, and {@code \"uncommon\"}, which clearly is\n+     * undesirable. Adding the group fixes this.\n+     *\n+     * <p>For simplicity the grouping is added to regular expressions unconditionally.\n+     *\n+     * @param input the input string.\n+     * @param alreadyRegex signals if input already is a regular expression.\n+     * @return a regex string.\n+     */\n+    private static String ensureSelfContainedRegex(final String input, final boolean alreadyRegex) {\n+        final String result;\n+        if (alreadyRegex) {\n+            result = encloseInGroup(input);\n+        }\n+        else {\n+            result = toRegex(input);\n+        }\n+        return result;\n+    }\n+\n+    /**\n+     * Enclose {@code expression} in a (non-capturing) group.\n+     * @param expression the input regular expression\n+     * @return a grouped pattern.\n+     */\n+    private static String encloseInGroup(String expression) {\n+        return \"(?:\" + expression + \")\";\n+    }\n+\n+    /**\n+     * Converts a normal package name into a regex pattern by escaping all\n+     * special characters that may occur in a java package name.\n+     * @param input the input string.\n+     * @return a regex string.\n+     */\n+    private static String toRegex(String input) {\n+        return DOT_PATTERN.matcher(input).replaceAll(DOT_REGEX);\n+    }\n+\n+    /**\n+     * Creates a Pattern from fullPackage that matches exactly and child packages.\n+     * @return a Pattern.\n+     */\n+    private Pattern createPatternForPartialMatch() {\n+        return Pattern.compile(encloseInGroup(fullPackage) + \"(?:\\\\..*)?\");","path":"src/main/java/com/puppycrawl/tools/checkstyle/checks/imports/PkgControl.java","position":null,"original_position":140,"commit_id":"8b5e2b29e4f040e026c66109664ebd14ef905ce3","original_commit_id":"5987b6f5a38b694e56445a80c860c399131db5db","user":{"login":"vboerchers","id":2280347,"avatar_url":"https://avatars.githubusercontent.com/u/2280347?v=3","gravatar_id":"","url":"https://api.github.com/users/vboerchers","html_url":"https://github.com/vboerchers","followers_url":"https://api.github.com/users/vboerchers/followers","following_url":"https://api.github.com/users/vboerchers/following{/other_user}","gists_url":"https://api.github.com/users/vboerchers/gists{/gist_id}","starred_url":"https://api.github.com/users/vboerchers/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vboerchers/subscriptions","organizations_url":"https://api.github.com/users/vboerchers/orgs","repos_url":"https://api.github.com/users/vboerchers/repos","events_url":"https://api.github.com/users/vboerchers/events{/privacy}","received_events_url":"https://api.github.com/users/vboerchers/received_events","type":"User","site_admin":false},"body":"hmm, it's a concatenation of regular expression - that is captured by a test of course. The change was necessary after I removed the grouping from the first constructor.\r\nDone.","created_at":"2016-10-15T16:50:01Z","updated_at":"2016-10-15T16:50:01Z","html_url":"https://github.com/checkstyle/checkstyle/pull/3438#discussion_r83535801","pull_request_url":"https://api.github.com/repos/checkstyle/checkstyle/pulls/3438","_links":{"self":{"href":"https://api.github.com/repos/checkstyle/checkstyle/pulls/comments/83535801"},"html":{"href":"https://github.com/checkstyle/checkstyle/pull/3438#discussion_r83535801"},"pull_request":{"href":"https://api.github.com/repos/checkstyle/checkstyle/pulls/3438"}}},"pull_request":{"url":"https://api.github.com/repos/checkstyle/checkstyle/pulls/3438","id":84789666,"html_url":"https://github.com/checkstyle/checkstyle/pull/3438","diff_url":"https://github.com/checkstyle/checkstyle/pull/3438.diff","patch_url":"https://github.com/checkstyle/checkstyle/pull/3438.patch","issue_url":"https://api.github.com/repos/checkstyle/checkstyle/issues/3438","number":3438,"state":"open","locked":false,"title":"Issue #2999: allow regex in importcontrol and subpackage element","user":{"login":"vboerchers","id":2280347,"avatar_url":"https://avatars.githubusercontent.com/u/2280347?v=3","gravatar_id":"","url":"https://api.github.com/users/vboerchers","html_url":"https://github.com/vboerchers","followers_url":"https://api.github.com/users/vboerchers/followers","following_url":"https://api.github.com/users/vboerchers/following{/other_user}","gists_url":"https://api.github.com/users/vboerchers/gists{/gist_id}","starred_url":"https://api.github.com/users/vboerchers/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vboerchers/subscriptions","organizations_url":"https://api.github.com/users/vboerchers/orgs","repos_url":"https://api.github.com/users/vboerchers/repos","events_url":"https://api.github.com/users/vboerchers/events{/privacy}","received_events_url":"https://api.github.com/users/vboerchers/received_events","type":"User","site_admin":false},"body":"This pull request fixes Issue #2999. Notes:\r\n* I have added the possibility to define packages not only to the `subpackage` element but also to the root element `importcontrol`.\r\n* The travis will continue to fail until the new DTD [import_control_1_2.dtd](https://github.com/vboerchers/checkstyle/blob/master/src/main/resources/com/puppycrawl/tools/checkstyle/checks/imports/import_control_1_2.dtd) is available as http://www.puppycrawl.com/dtds/import_control_1_2.dtd","created_at":"2016-09-11T15:38:31Z","updated_at":"2016-10-15T16:50:01Z","closed_at":null,"merged_at":null,"merge_commit_sha":"fc5cf562975c961e9819c0d81f8d9f2efcc724d3","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/checkstyle/checkstyle/pulls/3438/commits","review_comments_url":"https://api.github.com/repos/checkstyle/checkstyle/pulls/3438/comments","review_comment_url":"https://api.github.com/repos/checkstyle/checkstyle/pulls/comments{/number}","comments_url":"https://api.github.com/repos/checkstyle/checkstyle/issues/3438/comments","statuses_url":"https://api.github.com/repos/checkstyle/checkstyle/statuses/8b5e2b29e4f040e026c66109664ebd14ef905ce3","head":{"label":"vboerchers:master","ref":"master","sha":"8b5e2b29e4f040e026c66109664ebd14ef905ce3","user":{"login":"vboerchers","id":2280347,"avatar_url":"https://avatars.githubusercontent.com/u/2280347?v=3","gravatar_id":"","url":"https://api.github.com/users/vboerchers","html_url":"https://github.com/vboerchers","followers_url":"https://api.github.com/users/vboerchers/followers","following_url":"https://api.github.com/users/vboerchers/following{/other_user}","gists_url":"https://api.github.com/users/vboerchers/gists{/gist_id}","starred_url":"https://api.github.com/users/vboerchers/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vboerchers/subscriptions","organizations_url":"https://api.github.com/users/vboerchers/orgs","repos_url":"https://api.github.com/users/vboerchers/repos","events_url":"https://api.github.com/users/vboerchers/events{/privacy}","received_events_url":"https://api.github.com/users/vboerchers/received_events","type":"User","site_admin":false},"repo":{"id":67605731,"name":"checkstyle","full_name":"vboerchers/checkstyle","owner":{"login":"vboerchers","id":2280347,"avatar_url":"https://avatars.githubusercontent.com/u/2280347?v=3","gravatar_id":"","url":"https://api.github.com/users/vboerchers","html_url":"https://github.com/vboerchers","followers_url":"https://api.github.com/users/vboerchers/followers","following_url":"https://api.github.com/users/vboerchers/following{/other_user}","gists_url":"https://api.github.com/users/vboerchers/gists{/gist_id}","starred_url":"https://api.github.com/users/vboerchers/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vboerchers/subscriptions","organizations_url":"https://api.github.com/users/vboerchers/orgs","repos_url":"https://api.github.com/users/vboerchers/repos","events_url":"https://api.github.com/users/vboerchers/events{/privacy}","received_events_url":"https://api.github.com/users/vboerchers/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/vboerchers/checkstyle","description":"Checkstyle is a development tool to help programmers write Java code that adheres to a coding standard. By default it supports the Google Java Style Guide and Sun Code Conventions, but is highly configurable. It can be invoked with an ANT task and a command line program.","fork":true,"url":"https://api.github.com/repos/vboerchers/checkstyle","forks_url":"https://api.github.com/repos/vboerchers/checkstyle/forks","keys_url":"https://api.github.com/repos/vboerchers/checkstyle/keys{/key_id}","collaborators_url":"https://api.github.com/repos/vboerchers/checkstyle/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/vboerchers/checkstyle/teams","hooks_url":"https://api.github.com/repos/vboerchers/checkstyle/hooks","issue_events_url":"https://api.github.com/repos/vboerchers/checkstyle/issues/events{/number}","events_url":"https://api.github.com/repos/vboerchers/checkstyle/events","assignees_url":"https://api.github.com/repos/vboerchers/checkstyle/assignees{/user}","branches_url":"https://api.github.com/repos/vboerchers/checkstyle/branches{/branch}","tags_url":"https://api.github.com/repos/vboerchers/checkstyle/tags","blobs_url":"https://api.github.com/repos/vboerchers/checkstyle/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/vboerchers/checkstyle/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/vboerchers/checkstyle/git/refs{/sha}","trees_url":"https://api.github.com/repos/vboerchers/checkstyle/git/trees{/sha}","statuses_url":"https://api.github.com/repos/vboerchers/checkstyle/statuses/{sha}","languages_url":"https://api.github.com/repos/vboerchers/checkstyle/languages","stargazers_url":"https://api.github.com/repos/vboerchers/checkstyle/stargazers","contributors_url":"https://api.github.com/repos/vboerchers/checkstyle/contributors","subscribers_url":"https://api.github.com/repos/vboerchers/checkstyle/subscribers","subscription_url":"https://api.github.com/repos/vboerchers/checkstyle/subscription","commits_url":"https://api.github.com/repos/vboerchers/checkstyle/commits{/sha}","git_commits_url":"https://api.github.com/repos/vboerchers/checkstyle/git/commits{/sha}","comments_url":"https://api.github.com/repos/vboerchers/checkstyle/comments{/number}","issue_comment_url":"https://api.github.com/repos/vboerchers/checkstyle/issues/comments{/number}","contents_url":"https://api.github.com/repos/vboerchers/checkstyle/contents/{+path}","compare_url":"https://api.github.com/repos/vboerchers/checkstyle/compare/{base}...{head}","merges_url":"https://api.github.com/repos/vboerchers/checkstyle/merges","archive_url":"https://api.github.com/repos/vboerchers/checkstyle/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/vboerchers/checkstyle/downloads","issues_url":"https://api.github.com/repos/vboerchers/checkstyle/issues{/number}","pulls_url":"https://api.github.com/repos/vboerchers/checkstyle/pulls{/number}","milestones_url":"https://api.github.com/repos/vboerchers/checkstyle/milestones{/number}","notifications_url":"https://api.github.com/repos/vboerchers/checkstyle/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/vboerchers/checkstyle/labels{/name}","releases_url":"https://api.github.com/repos/vboerchers/checkstyle/releases{/id}","deployments_url":"https://api.github.com/repos/vboerchers/checkstyle/deployments","created_at":"2016-09-07T12:46:13Z","updated_at":"2016-09-07T12:46:19Z","pushed_at":"2016-10-15T16:47:02Z","git_url":"git://github.com/vboerchers/checkstyle.git","ssh_url":"git@github.com:vboerchers/checkstyle.git","clone_url":"https://github.com/vboerchers/checkstyle.git","svn_url":"https://github.com/vboerchers/checkstyle","homepage":"http://checkstyle.sourceforge.net/","size":51930,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"checkstyle:master","ref":"master","sha":"864060df9737a3ca5eb8eb26b25b247158cb6ad4","user":{"login":"checkstyle","id":5179750,"avatar_url":"https://avatars.githubusercontent.com/u/5179750?v=3","gravatar_id":"","url":"https://api.github.com/users/checkstyle","html_url":"https://github.com/checkstyle","followers_url":"https://api.github.com/users/checkstyle/followers","following_url":"https://api.github.com/users/checkstyle/following{/other_user}","gists_url":"https://api.github.com/users/checkstyle/gists{/gist_id}","starred_url":"https://api.github.com/users/checkstyle/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/checkstyle/subscriptions","organizations_url":"https://api.github.com/users/checkstyle/orgs","repos_url":"https://api.github.com/users/checkstyle/repos","events_url":"https://api.github.com/users/checkstyle/events{/privacy}","received_events_url":"https://api.github.com/users/checkstyle/received_events","type":"Organization","site_admin":false},"repo":{"id":12499251,"name":"checkstyle","full_name":"checkstyle/checkstyle","owner":{"login":"checkstyle","id":5179750,"avatar_url":"https://avatars.githubusercontent.com/u/5179750?v=3","gravatar_id":"","url":"https://api.github.com/users/checkstyle","html_url":"https://github.com/checkstyle","followers_url":"https://api.github.com/users/checkstyle/followers","following_url":"https://api.github.com/users/checkstyle/following{/other_user}","gists_url":"https://api.github.com/users/checkstyle/gists{/gist_id}","starred_url":"https://api.github.com/users/checkstyle/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/checkstyle/subscriptions","organizations_url":"https://api.github.com/users/checkstyle/orgs","repos_url":"https://api.github.com/users/checkstyle/repos","events_url":"https://api.github.com/users/checkstyle/events{/privacy}","received_events_url":"https://api.github.com/users/checkstyle/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/checkstyle/checkstyle","description":"Checkstyle is a development tool to help programmers write Java code that adheres to a coding standard. By default it supports the Google Java Style Guide and Sun Code Conventions, but is highly configurable. It can be invoked with an ANT task and a command line program.","fork":false,"url":"https://api.github.com/repos/checkstyle/checkstyle","forks_url":"https://api.github.com/repos/checkstyle/checkstyle/forks","keys_url":"https://api.github.com/repos/checkstyle/checkstyle/keys{/key_id}","collaborators_url":"https://api.github.com/repos/checkstyle/checkstyle/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/checkstyle/checkstyle/teams","hooks_url":"https://api.github.com/repos/checkstyle/checkstyle/hooks","issue_events_url":"https://api.github.com/repos/checkstyle/checkstyle/issues/events{/number}","events_url":"https://api.github.com/repos/checkstyle/checkstyle/events","assignees_url":"https://api.github.com/repos/checkstyle/checkstyle/assignees{/user}","branches_url":"https://api.github.com/repos/checkstyle/checkstyle/branches{/branch}","tags_url":"https://api.github.com/repos/checkstyle/checkstyle/tags","blobs_url":"https://api.github.com/repos/checkstyle/checkstyle/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/checkstyle/checkstyle/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/checkstyle/checkstyle/git/refs{/sha}","trees_url":"https://api.github.com/repos/checkstyle/checkstyle/git/trees{/sha}","statuses_url":"https://api.github.com/repos/checkstyle/checkstyle/statuses/{sha}","languages_url":"https://api.github.com/repos/checkstyle/checkstyle/languages","stargazers_url":"https://api.github.com/repos/checkstyle/checkstyle/stargazers","contributors_url":"https://api.github.com/repos/checkstyle/checkstyle/contributors","subscribers_url":"https://api.github.com/repos/checkstyle/checkstyle/subscribers","subscription_url":"https://api.github.com/repos/checkstyle/checkstyle/subscription","commits_url":"https://api.github.com/repos/checkstyle/checkstyle/commits{/sha}","git_commits_url":"https://api.github.com/repos/checkstyle/checkstyle/git/commits{/sha}","comments_url":"https://api.github.com/repos/checkstyle/checkstyle/comments{/number}","issue_comment_url":"https://api.github.com/repos/checkstyle/checkstyle/issues/comments{/number}","contents_url":"https://api.github.com/repos/checkstyle/checkstyle/contents/{+path}","compare_url":"https://api.github.com/repos/checkstyle/checkstyle/compare/{base}...{head}","merges_url":"https://api.github.com/repos/checkstyle/checkstyle/merges","archive_url":"https://api.github.com/repos/checkstyle/checkstyle/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/checkstyle/checkstyle/downloads","issues_url":"https://api.github.com/repos/checkstyle/checkstyle/issues{/number}","pulls_url":"https://api.github.com/repos/checkstyle/checkstyle/pulls{/number}","milestones_url":"https://api.github.com/repos/checkstyle/checkstyle/milestones{/number}","notifications_url":"https://api.github.com/repos/checkstyle/checkstyle/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/checkstyle/checkstyle/labels{/name}","releases_url":"https://api.github.com/repos/checkstyle/checkstyle/releases{/id}","deployments_url":"https://api.github.com/repos/checkstyle/checkstyle/deployments","created_at":"2013-08-31T02:05:05Z","updated_at":"2016-10-15T09:47:46Z","pushed_at":"2016-10-15T16:47:03Z","git_url":"git://github.com/checkstyle/checkstyle.git","ssh_url":"git@github.com:checkstyle/checkstyle.git","clone_url":"https://github.com/checkstyle/checkstyle.git","svn_url":"https://github.com/checkstyle/checkstyle","homepage":"http://checkstyle.sourceforge.net/","size":58577,"stargazers_count":1377,"watchers_count":1377,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":628,"mirror_url":null,"open_issues_count":301,"forks":628,"open_issues":301,"watchers":1377,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/checkstyle/checkstyle/pulls/3438"},"html":{"href":"https://github.com/checkstyle/checkstyle/pull/3438"},"issue":{"href":"https://api.github.com/repos/checkstyle/checkstyle/issues/3438"},"comments":{"href":"https://api.github.com/repos/checkstyle/checkstyle/issues/3438/comments"},"review_comments":{"href":"https://api.github.com/repos/checkstyle/checkstyle/pulls/3438/comments"},"review_comment":{"href":"https://api.github.com/repos/checkstyle/checkstyle/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/checkstyle/checkstyle/pulls/3438/commits"},"statuses":{"href":"https://api.github.com/repos/checkstyle/checkstyle/statuses/8b5e2b29e4f040e026c66109664ebd14ef905ce3"}}}}
{"actor":{"display_login":"vboerchers"}}