{"action":"created","comment":{"url":"https://api.github.com/repos/zanata/zanata-client/pulls/comments/29488240","id":29488240,"diff_hunk":"@@ -75,10 +96,174 @@ public RawPushCommand(PushOptions opts, RestClientFactory clientFactory) {\n         client = getClientFactory().getFileResourceClient();\n     }\n \n+    /**\n+     * Extract extensions in square bracket\n+     * input pattern: [extension;extension]\n+     */\n+    private List<String> extractExtensions(String rawExtensions) {\n+        if(StringUtils.isEmpty(rawExtensions)) {\n+            return Collections.emptyList();\n+        }\n+\n+        //remove square bracket in first and last character\n+        rawExtensions = rawExtensions.substring(1, rawExtensions.length()-1);\n+        return Arrays.asList(rawExtensions.split(\";\"));\n+    }\n+\n+    /**\n+     * Extract input string to type and extensions\n+     * input pattern: fileType[extension;extension], [extension;extension]\n+     *\n+     * @param typeWithExtension\n+     * @return result[0] - type, result[1] - extensions. e.g [txt,html]\n+     */\n+    private String[] extractTypeAndExtension(String typeWithExtension) {\n+        String[] result = new String[2];\n+\n+        final Pattern fileNameExtensionsPattern = Pattern.compile(\"(.*)?(\\\\[(.*?)\\\\])?\");\n+        Matcher matcher = fileNameExtensionsPattern.matcher(typeWithExtension);\n+        if (matcher.find()) {\n+            result[0] = matcher.group(1);\n+            result[1] = matcher.group(2);\n+        }\n+        return result;\n+    }\n+\n+    /**\n+     * Validate inputFileType to make sure it's not empty\n+     *\n+     * @param inputFileType\n+     * @param inputExtensions\n+     * @param acceptedTypes\n+     */\n+    private void validateInputFileType(String inputFileType,\n+            List<String> inputExtensions, List<DocumentType> acceptedTypes) {\n+        if (StringUtils.isNotBlank(inputFileType)) {\n+            return;\n+        }\n+        if (inputExtensions.isEmpty()) {\n+            //throw error if inputFileType and inputExtensions is empty\n+            throw new InvalidUserInputException(\n+                    \"Invalid expression for '--file-types' option\");\n+        } else {\n+            //suggest --file-types options for this extension\n+            for (DocumentType docType: acceptedTypes) {\n+                for (String extension: docType.getSourceExtensions()) {\n+                    if (inputExtensions.contains(extension)) {\n+                        String msg =\n+                                Messages.format(\n+                                        \"file.type.suggestFromExtension\",\n+                                        docType,\n+                            extension, docType);\n+                        throw new InvalidUserInputException(msg);\n+                    }\n+                }\n+            }\n+        }\n+    }\n+\n+    /**\n+     * Return map of validated type with set of extensions\n+     *\n+     * Validate user input file types against server accepted file types\n+     *\n+     * Valid input - properties_utf8,properties[txt],plain_text[md;markdown]\n+     *\n+     * - Each file type is only input once - e.g. html,properties,html\n+     * - Same file extension cannot be in multiple file type - e.g. plain_text[txt],properties[txt]\n+     * - Generate warning and ignore for source file extension that is not supported from file type. - e.g idml[txt]\n+     *\n+     * @param acceptedTypes\n+     * @param inputFileTypes\n+     */\n+    private Map<DocumentType, Set<String>> validateFileTypes(\n+            List<DocumentType> acceptedTypes, List<String> inputFileTypes) {\n+\n+        Map<DocumentType, Set<String>> filteredFileTypes = new HashMap<>();\n+\n+        for (String typeWithExtension : inputFileTypes) {\n+            String[] fileTypeAndExtensions = extractTypeAndExtension(typeWithExtension);\n+\n+            String fileType = fileTypeAndExtensions[0];\n+            List<String> extensions = extractExtensions(fileTypeAndExtensions[1]);\n+\n+            validateInputFileType(fileType, extensions, acceptedTypes);\n+\n+            DocumentType inputFileType = DocumentType.getByName(fileType);\n+\n+            //skip file type if its not supported\n+            if (fileType == null) {\n+                String msg = Messages.format(\"file.type.typeNotSupported\", fileType);\n+                consoleInteractor.printfln(DisplayMode.Warning, msg);\n+                continue;\n+            }\n+\n+            //throw error if file type is input more than once\n+            if (filteredFileTypes.containsKey(inputFileType)) {\n+                String msg =\n+                        Messages.format(\"file.type.duplicateFileType\",\n+                                fileType);\n+                log.error(msg);\n+                throw new RuntimeException(msg);\n+            }\n+\n+            Set<String> filteredExtensions = new HashSet<>(extensions);\n+\n+            validateSourceExtension(extensions, filteredExtensions,\n+                    inputFileType);\n+\n+            /**\n+             * Use the extensions from typeWithExtension input if exists,\n+             * otherwise, use the extensions from server.\n+             */\n+            filteredExtensions =\n+                filteredExtensions.isEmpty() ? inputFileType.getSourceExtensions()\n+                            : filteredExtensions;\n+\n+            //throw error if same file extension found in multiple file type\n+            for (DocumentType filteredDocType: filteredFileTypes.keySet()) {\n+                for (String filteredExtension: filteredDocType.getSourceExtensions()) {\n+                    if (filteredExtensions.contains(filteredExtension)) {\n+                        String msg =\n+                                Messages.format(\n+                                        \"file.type.conflictExtension\",\n+                                        fileType, filteredDocType.name(),\n+                                        filteredExtension);\n+                        log.error(msg);\n+                        throw new RuntimeException(msg);\n+                    }\n+                }\n+            }\n+            filteredFileTypes.put(inputFileType, filteredExtensions);\n+        }\n+        return filteredFileTypes;\n+    }\n+\n+    /**\n+     * throw exception if user input extension if not part of DocumentType supported source extension\n+     * @param extensions\n+     * @param filteredExtensions\n+     * @param inputFileType\n+     */\n+    private void validateSourceExtension(List<String> extensions,\n+            Set<String> filteredExtensions, DocumentType inputFileType) {\n+        if (!extensions.isEmpty()) {\n+            for (String extension: extensions) {\n+                if (!inputFileType.getSourceExtensions()\n+                    .contains(extension)) {\n+                    String msg = Messages.format(\n+                        \"file.type.notSupportedExtension\", extension,\n+                        inputFileType.name());\n+                    consoleInteractor.printfln(DisplayMode.Warning, msg);\n+                    filteredExtensions.remove(extension);","path":"zanata-client-commands/src/main/java/org/zanata/client/commands/push/RawPushCommand.java","position":220,"original_position":220,"commit_id":"13a1146aec474f494fb09c793367e71f2fd36187","original_commit_id":"13a1146aec474f494fb09c793367e71f2fd36187","user":{"login":"davidmason","id":482026,"avatar_url":"https://avatars.githubusercontent.com/u/482026?v=3","gravatar_id":"","url":"https://api.github.com/users/davidmason","html_url":"https://github.com/davidmason","followers_url":"https://api.github.com/users/davidmason/followers","following_url":"https://api.github.com/users/davidmason/following{/other_user}","gists_url":"https://api.github.com/users/davidmason/gists{/gist_id}","starred_url":"https://api.github.com/users/davidmason/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/davidmason/subscriptions","organizations_url":"https://api.github.com/users/davidmason/orgs","repos_url":"https://api.github.com/users/davidmason/repos","events_url":"https://api.github.com/users/davidmason/events{/privacy}","received_events_url":"https://api.github.com/users/davidmason/received_events","type":"User","site_admin":false},"body":"- [ ] We should accept custom extensions, as long as the user specified a file type so we know what type to send the file as.","created_at":"2015-05-01T03:33:44Z","updated_at":"2015-05-01T03:33:44Z","html_url":"https://github.com/zanata/zanata-client/pull/57#discussion_r29488240","pull_request_url":"https://api.github.com/repos/zanata/zanata-client/pulls/57","_links":{"self":{"href":"https://api.github.com/repos/zanata/zanata-client/pulls/comments/29488240"},"html":{"href":"https://github.com/zanata/zanata-client/pull/57#discussion_r29488240"},"pull_request":{"href":"https://api.github.com/repos/zanata/zanata-client/pulls/57"}}},"pull_request":{"url":"https://api.github.com/repos/zanata/zanata-client/pulls/57","id":32917829,"html_url":"https://github.com/zanata/zanata-client/pull/57","diff_url":"https://github.com/zanata/zanata-client/pull/57.diff","patch_url":"https://github.com/zanata/zanata-client/pull/57.patch","issue_url":"https://api.github.com/repos/zanata/zanata-client/issues/57","number":57,"state":"open","locked":false,"title":"Implement doc type support for file projec type","user":{"login":"aeng","id":766762,"avatar_url":"https://avatars.githubusercontent.com/u/766762?v=3","gravatar_id":"","url":"https://api.github.com/users/aeng","html_url":"https://github.com/aeng","followers_url":"https://api.github.com/users/aeng/followers","following_url":"https://api.github.com/users/aeng/following{/other_user}","gists_url":"https://api.github.com/users/aeng/gists{/gist_id}","starred_url":"https://api.github.com/users/aeng/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aeng/subscriptions","organizations_url":"https://api.github.com/users/aeng/orgs","repos_url":"https://api.github.com/users/aeng/repos","events_url":"https://api.github.com/users/aeng/events{/privacy}","received_events_url":"https://api.github.com/users/aeng/received_events","type":"User","site_admin":false},"body":"https://bugzilla.redhat.com/show_bug.cgi?id=1186972\r\n\r\n1. common: https://github.com/zanata/zanata-common/pull/9\r\n2. api: https://github.com/zanata/zanata-api/pull/26\r\n3. **client**: https://github.com/zanata/zanata-client/pull/57\r\n4. server: https://github.com/zanata/zanata-server/pull/772","created_at":"2015-04-09T01:23:23Z","updated_at":"2015-05-01T03:33:44Z","closed_at":null,"merged_at":null,"merge_commit_sha":"bf9abd0425341603e44ee7ab80a9db2c0bc2b1fd","assignee":{"login":"davidmason","id":482026,"avatar_url":"https://avatars.githubusercontent.com/u/482026?v=3","gravatar_id":"","url":"https://api.github.com/users/davidmason","html_url":"https://github.com/davidmason","followers_url":"https://api.github.com/users/davidmason/followers","following_url":"https://api.github.com/users/davidmason/following{/other_user}","gists_url":"https://api.github.com/users/davidmason/gists{/gist_id}","starred_url":"https://api.github.com/users/davidmason/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/davidmason/subscriptions","organizations_url":"https://api.github.com/users/davidmason/orgs","repos_url":"https://api.github.com/users/davidmason/repos","events_url":"https://api.github.com/users/davidmason/events{/privacy}","received_events_url":"https://api.github.com/users/davidmason/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/zanata/zanata-client/pulls/57/commits","review_comments_url":"https://api.github.com/repos/zanata/zanata-client/pulls/57/comments","review_comment_url":"https://api.github.com/repos/zanata/zanata-client/pulls/comments{/number}","comments_url":"https://api.github.com/repos/zanata/zanata-client/issues/57/comments","statuses_url":"https://api.github.com/repos/zanata/zanata-client/statuses/13a1146aec474f494fb09c793367e71f2fd36187","head":{"label":"zanata:rhbz1186972","ref":"rhbz1186972","sha":"13a1146aec474f494fb09c793367e71f2fd36187","user":{"login":"zanata","id":637455,"avatar_url":"https://avatars.githubusercontent.com/u/637455?v=3","gravatar_id":"","url":"https://api.github.com/users/zanata","html_url":"https://github.com/zanata","followers_url":"https://api.github.com/users/zanata/followers","following_url":"https://api.github.com/users/zanata/following{/other_user}","gists_url":"https://api.github.com/users/zanata/gists{/gist_id}","starred_url":"https://api.github.com/users/zanata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zanata/subscriptions","organizations_url":"https://api.github.com/users/zanata/orgs","repos_url":"https://api.github.com/users/zanata/repos","events_url":"https://api.github.com/users/zanata/events{/privacy}","received_events_url":"https://api.github.com/users/zanata/received_events","type":"Organization","site_admin":false},"repo":{"id":4119384,"name":"zanata-client","full_name":"zanata/zanata-client","owner":{"login":"zanata","id":637455,"avatar_url":"https://avatars.githubusercontent.com/u/637455?v=3","gravatar_id":"","url":"https://api.github.com/users/zanata","html_url":"https://github.com/zanata","followers_url":"https://api.github.com/users/zanata/followers","following_url":"https://api.github.com/users/zanata/following{/other_user}","gists_url":"https://api.github.com/users/zanata/gists{/gist_id}","starred_url":"https://api.github.com/users/zanata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zanata/subscriptions","organizations_url":"https://api.github.com/users/zanata/orgs","repos_url":"https://api.github.com/users/zanata/repos","events_url":"https://api.github.com/users/zanata/events{/privacy}","received_events_url":"https://api.github.com/users/zanata/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/zanata/zanata-client","description":"Zanata client modules","fork":false,"url":"https://api.github.com/repos/zanata/zanata-client","forks_url":"https://api.github.com/repos/zanata/zanata-client/forks","keys_url":"https://api.github.com/repos/zanata/zanata-client/keys{/key_id}","collaborators_url":"https://api.github.com/repos/zanata/zanata-client/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/zanata/zanata-client/teams","hooks_url":"https://api.github.com/repos/zanata/zanata-client/hooks","issue_events_url":"https://api.github.com/repos/zanata/zanata-client/issues/events{/number}","events_url":"https://api.github.com/repos/zanata/zanata-client/events","assignees_url":"https://api.github.com/repos/zanata/zanata-client/assignees{/user}","branches_url":"https://api.github.com/repos/zanata/zanata-client/branches{/branch}","tags_url":"https://api.github.com/repos/zanata/zanata-client/tags","blobs_url":"https://api.github.com/repos/zanata/zanata-client/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/zanata/zanata-client/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/zanata/zanata-client/git/refs{/sha}","trees_url":"https://api.github.com/repos/zanata/zanata-client/git/trees{/sha}","statuses_url":"https://api.github.com/repos/zanata/zanata-client/statuses/{sha}","languages_url":"https://api.github.com/repos/zanata/zanata-client/languages","stargazers_url":"https://api.github.com/repos/zanata/zanata-client/stargazers","contributors_url":"https://api.github.com/repos/zanata/zanata-client/contributors","subscribers_url":"https://api.github.com/repos/zanata/zanata-client/subscribers","subscription_url":"https://api.github.com/repos/zanata/zanata-client/subscription","commits_url":"https://api.github.com/repos/zanata/zanata-client/commits{/sha}","git_commits_url":"https://api.github.com/repos/zanata/zanata-client/git/commits{/sha}","comments_url":"https://api.github.com/repos/zanata/zanata-client/comments{/number}","issue_comment_url":"https://api.github.com/repos/zanata/zanata-client/issues/comments{/number}","contents_url":"https://api.github.com/repos/zanata/zanata-client/contents/{+path}","compare_url":"https://api.github.com/repos/zanata/zanata-client/compare/{base}...{head}","merges_url":"https://api.github.com/repos/zanata/zanata-client/merges","archive_url":"https://api.github.com/repos/zanata/zanata-client/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/zanata/zanata-client/downloads","issues_url":"https://api.github.com/repos/zanata/zanata-client/issues{/number}","pulls_url":"https://api.github.com/repos/zanata/zanata-client/pulls{/number}","milestones_url":"https://api.github.com/repos/zanata/zanata-client/milestones{/number}","notifications_url":"https://api.github.com/repos/zanata/zanata-client/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/zanata/zanata-client/labels{/name}","releases_url":"https://api.github.com/repos/zanata/zanata-client/releases{/id}","created_at":"2012-04-24T00:00:28Z","updated_at":"2015-04-29T05:46:40Z","pushed_at":"2015-04-29T05:46:40Z","git_url":"git://github.com/zanata/zanata-client.git","ssh_url":"git@github.com:zanata/zanata-client.git","clone_url":"https://github.com/zanata/zanata-client.git","svn_url":"https://github.com/zanata/zanata-client","homepage":null,"size":5699,"stargazers_count":4,"watchers_count":4,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":3,"mirror_url":null,"open_issues_count":1,"forks":3,"open_issues":1,"watchers":4,"default_branch":"master"}},"base":{"label":"zanata:integration/master","ref":"integration/master","sha":"01b65423f8b8cf30b84a4845d23faec5c0ddeb49","user":{"login":"zanata","id":637455,"avatar_url":"https://avatars.githubusercontent.com/u/637455?v=3","gravatar_id":"","url":"https://api.github.com/users/zanata","html_url":"https://github.com/zanata","followers_url":"https://api.github.com/users/zanata/followers","following_url":"https://api.github.com/users/zanata/following{/other_user}","gists_url":"https://api.github.com/users/zanata/gists{/gist_id}","starred_url":"https://api.github.com/users/zanata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zanata/subscriptions","organizations_url":"https://api.github.com/users/zanata/orgs","repos_url":"https://api.github.com/users/zanata/repos","events_url":"https://api.github.com/users/zanata/events{/privacy}","received_events_url":"https://api.github.com/users/zanata/received_events","type":"Organization","site_admin":false},"repo":{"id":4119384,"name":"zanata-client","full_name":"zanata/zanata-client","owner":{"login":"zanata","id":637455,"avatar_url":"https://avatars.githubusercontent.com/u/637455?v=3","gravatar_id":"","url":"https://api.github.com/users/zanata","html_url":"https://github.com/zanata","followers_url":"https://api.github.com/users/zanata/followers","following_url":"https://api.github.com/users/zanata/following{/other_user}","gists_url":"https://api.github.com/users/zanata/gists{/gist_id}","starred_url":"https://api.github.com/users/zanata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zanata/subscriptions","organizations_url":"https://api.github.com/users/zanata/orgs","repos_url":"https://api.github.com/users/zanata/repos","events_url":"https://api.github.com/users/zanata/events{/privacy}","received_events_url":"https://api.github.com/users/zanata/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/zanata/zanata-client","description":"Zanata client modules","fork":false,"url":"https://api.github.com/repos/zanata/zanata-client","forks_url":"https://api.github.com/repos/zanata/zanata-client/forks","keys_url":"https://api.github.com/repos/zanata/zanata-client/keys{/key_id}","collaborators_url":"https://api.github.com/repos/zanata/zanata-client/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/zanata/zanata-client/teams","hooks_url":"https://api.github.com/repos/zanata/zanata-client/hooks","issue_events_url":"https://api.github.com/repos/zanata/zanata-client/issues/events{/number}","events_url":"https://api.github.com/repos/zanata/zanata-client/events","assignees_url":"https://api.github.com/repos/zanata/zanata-client/assignees{/user}","branches_url":"https://api.github.com/repos/zanata/zanata-client/branches{/branch}","tags_url":"https://api.github.com/repos/zanata/zanata-client/tags","blobs_url":"https://api.github.com/repos/zanata/zanata-client/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/zanata/zanata-client/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/zanata/zanata-client/git/refs{/sha}","trees_url":"https://api.github.com/repos/zanata/zanata-client/git/trees{/sha}","statuses_url":"https://api.github.com/repos/zanata/zanata-client/statuses/{sha}","languages_url":"https://api.github.com/repos/zanata/zanata-client/languages","stargazers_url":"https://api.github.com/repos/zanata/zanata-client/stargazers","contributors_url":"https://api.github.com/repos/zanata/zanata-client/contributors","subscribers_url":"https://api.github.com/repos/zanata/zanata-client/subscribers","subscription_url":"https://api.github.com/repos/zanata/zanata-client/subscription","commits_url":"https://api.github.com/repos/zanata/zanata-client/commits{/sha}","git_commits_url":"https://api.github.com/repos/zanata/zanata-client/git/commits{/sha}","comments_url":"https://api.github.com/repos/zanata/zanata-client/comments{/number}","issue_comment_url":"https://api.github.com/repos/zanata/zanata-client/issues/comments{/number}","contents_url":"https://api.github.com/repos/zanata/zanata-client/contents/{+path}","compare_url":"https://api.github.com/repos/zanata/zanata-client/compare/{base}...{head}","merges_url":"https://api.github.com/repos/zanata/zanata-client/merges","archive_url":"https://api.github.com/repos/zanata/zanata-client/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/zanata/zanata-client/downloads","issues_url":"https://api.github.com/repos/zanata/zanata-client/issues{/number}","pulls_url":"https://api.github.com/repos/zanata/zanata-client/pulls{/number}","milestones_url":"https://api.github.com/repos/zanata/zanata-client/milestones{/number}","notifications_url":"https://api.github.com/repos/zanata/zanata-client/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/zanata/zanata-client/labels{/name}","releases_url":"https://api.github.com/repos/zanata/zanata-client/releases{/id}","created_at":"2012-04-24T00:00:28Z","updated_at":"2015-04-29T05:46:40Z","pushed_at":"2015-04-29T05:46:40Z","git_url":"git://github.com/zanata/zanata-client.git","ssh_url":"git@github.com:zanata/zanata-client.git","clone_url":"https://github.com/zanata/zanata-client.git","svn_url":"https://github.com/zanata/zanata-client","homepage":null,"size":5699,"stargazers_count":4,"watchers_count":4,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":3,"mirror_url":null,"open_issues_count":1,"forks":3,"open_issues":1,"watchers":4,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/zanata/zanata-client/pulls/57"},"html":{"href":"https://github.com/zanata/zanata-client/pull/57"},"issue":{"href":"https://api.github.com/repos/zanata/zanata-client/issues/57"},"comments":{"href":"https://api.github.com/repos/zanata/zanata-client/issues/57/comments"},"review_comments":{"href":"https://api.github.com/repos/zanata/zanata-client/pulls/57/comments"},"review_comment":{"href":"https://api.github.com/repos/zanata/zanata-client/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/zanata/zanata-client/pulls/57/commits"},"statuses":{"href":"https://api.github.com/repos/zanata/zanata-client/statuses/13a1146aec474f494fb09c793367e71f2fd36187"}}}}