[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] javaimp_devel 5e69ed1: javaimp: Rewrite Maven output parsing code
From: |
Filipp Gunbin |
Subject: |
[elpa] javaimp_devel 5e69ed1: javaimp: Rewrite Maven output parsing code |
Date: |
Fri, 18 Mar 2016 19:59:05 +0000 |
branch: javaimp_devel
commit 5e69ed13eec3fa055e86cf91f789ca167360b6ac
Author: Filipp Gunbin <address@hidden>
Commit: Filipp Gunbin <address@hidden>
javaimp: Rewrite Maven output parsing code
---
packages/javaimp/javaimp.el | 350 ++++++++++++++++++++++++-------------------
1 files changed, 193 insertions(+), 157 deletions(-)
diff --git a/packages/javaimp/javaimp.el b/packages/javaimp/javaimp.el
index df402ce..f05629a 100644
--- a/packages/javaimp/javaimp.el
+++ b/packages/javaimp/javaimp.el
@@ -1,6 +1,6 @@
;;; javaimp.el --- Add and reorder Java import statements in Maven projects
-*- lexical-binding: t; -*-
-;; Copyright (C) 2014, 2015 Free Software Foundation, Inc.
+;; Copyright (C) 2014, 2015, 2016 Free Software Foundation, Inc.
;; Author: Filipp Gunbin <address@hidden>
;; Maintainer: Filipp Gunbin <address@hidden>
@@ -66,11 +66,12 @@
;; which are not matched by any regexp in that variable are assigned a
;; default order defined by `javaimp-import-default-order' (50 by default).
;;
-;; Sample setup (put this into your .emacs):
+;; Sample .emacs initialization:
;;
;; (require 'javaimp)
;;
-;; (add-to-list 'javaimp-import-group-alist
'("\\`\\(ru\\.yota\\.\\|tv\\.okko\\.\\)" . 80))
+;; (add-to-list 'javaimp-import-group-alist
+;; '("\\`\\(my\\.company\\.\\|my\\.company2\\.\\)" . 80))
;;
;; (setq javaimp-jdk-home (getenv "JAVA_HOME"))
;; (setq javaimp-include-current-project-classes t)
@@ -82,19 +83,28 @@
;; (local-set-key "\C-co" 'javaimp-organize-imports)))
;;
;;
-;; TODO:
+;; TODO before version 1.0:
;;
-;; Support adding static imports by giving a prefix argument to
+;; - correct submodule tree for each top-level project (now top-level projects
+;; hold linear submodule list and this prevents modification checking for
parent
+;; poms). If a project doesn't have any children, then it should be in the
list
+;; by itself.
+;;
+;; - cl-defstruct for data
+;;
+;; Other:
+;;
+;; - support adding static imports by giving a prefix argument to
;; `javaimp-add-import'.
;;
-;; Use functions `cygwin-convert-file-name-from-windows' and
+;; - use functions `cygwin-convert-file-name-from-windows' and
;; `cygwin-convert-file-name-to-windows' when they are available instead of
-;; calling `cygpath'. See
-;; https://cygwin.com/ml/cygwin/2013-03/msg00228.html.
-
+;; calling `cygpath'. See https://cygwin.com/ml/cygwin/2013-03/msg00228.html.
;;; Code:
+(require 'seq)
+
;;; User options
@@ -163,7 +173,7 @@ Only top-level classes are included.")
(defconst javaimp-debug-buf-name "*javaimp-debug*")
-;;; Dealing with XML
+;;; XML routines
(defun javaimp-xml-child-list (xml-tree child-name)
"Returns list of children of XML-TREE filtered by CHILD-NAME"
@@ -182,8 +192,10 @@ Only top-level classes are included.")
(car (cddr el)))
-;; A module is represented as a list of the form `(ARTIFACT POM-FILE
-;; SOURCE-DIR TEST-SOURCE-DIR BUILD-DIR POM-FILE-MOD-TS PARENT PARENT-TS)'.
+;; FIXME: use cl-defstruct!
+
+;; A module is represented as a list: `(ARTIFACT POM-FILE SOURCE-DIR
+;; TEST-SOURCE-DIR BUILD-DIR POM-FILE-MOD-TS PARENT PARENT-TS)'.
(defsubst javaimp-make-mod (artifact pom-file source-dir
test-source-dir build-dir
@@ -197,6 +209,8 @@ Only top-level classes are included.")
(defsubst javaimp-get-mod-pom-file (module)
(nth 1 module))
+(defsubst javaimp-set-mod-pom-file (module value)
+ (setcar (nthcdr 1 module) value))
(defsubst javaimp-get-mod-source-dir (module)
(nth 2 module))
@@ -230,18 +244,16 @@ Only top-level classes are included.")
;; An artifact is represented as a list: (GROUP-ID ARTIFACT-ID VERSION).
-;; FIXME: use cl-defstruct!
-
-(defun javaimp-make-artifact (group-id artifact-id version)
+(defsubst javaimp-make-artifact (group-id artifact-id version)
(list group-id artifact-id version))
-(defun javaimp-artifact-group-id (artifact)
+(defsubst javaimp-artifact-group-id (artifact)
(car artifact))
-(defun javaimp-artifact-artifact-id (artifact)
+(defsubst javaimp-artifact-artifact-id (artifact)
(cadr artifact))
-(defun javaimp-artifact-version (artifact)
+(defsubst javaimp-artifact-version (artifact)
(nth 2 artifact))
(defun javaimp-artifact-to-string (artifact)
@@ -255,7 +267,7 @@ Only top-level classes are included.")
-;; A jar is represented as follows: `(JAR-PATH JAR-MOD-TS . CLASSES-LIST).
+;; A jar is represented as a list: `(JAR-PATH JAR-MOD-TS . CLASSES-LIST).
(defsubst javaimp-make-jar (jar-path jar-mod-ts classes-list)
(cons jar-path (cons jar-mod-ts classes-list)))
@@ -276,150 +288,168 @@ Only top-level classes are included.")
(setcdr (cdr jar) value))
-;;; Loading maven projects tree
+;;; Loading Maven projects tree
;;;###autoload
(defun javaimp-maven-visit-root (path)
"Loads all modules starting from root module identified by
-PATH. PATH should point to a directory."
+PATH. PATH should point to a directory containing pom.xml."
(interactive "DVisit maven root project: ")
- (let ((root-pom (expand-file-name
- (concat (file-name-as-directory path) "pom.xml")))
- modules existing-module)
- (unless (file-readable-p root-pom)
- (error "Cannot read root pom: %s" root-pom))
- (setq modules (javaimp-maven-load-module-tree root-pom))
- ;; if a root module with such path is already loaded, replace its
- ;; modules
- (setq existing-module (assoc root-pom javaimp-maven-root-modules))
- (if existing-module
- (setcdr existing-module modules)
+ (let* ((root-pom (expand-file-name
+ (concat (file-name-as-directory path) "pom.xml")))
+ (modules (if (file-readable-p root-pom)
+ (javaimp-maven-load-module-tree root-pom)
+ (error "Cannot read root pom: %s" root-pom)))
+ (root-project (assoc root-pom javaimp-maven-root-modules)))
+ (if root-project
+ (setcdr root-project modules)
(push (cons root-pom modules) javaimp-maven-root-modules))
(message "Loaded modules for %s" path)))
-(defun javaimp-get-projects (xml-tree)
- (cond ((assq 'projects xml-tree)
- (javaimp-xml-child-list (assq 'projects xml-tree) 'project))
- ((assq 'project xml-tree)
- (list (assq 'project xml-tree)))
- (t
- (error "Cannot find projects in mvn output"))))
-
-(defun javaimp-maven-load-module-tree (pom)
- "Returns an alist of all Maven modules in a hierarchy starting
-with POM"
+(defun javaimp-parse-effective-pom (pom)
+ "Calls `mvn help:effective:pom and returns XML parse tree"
(message "Loading root pom %s..." pom)
(javaimp-call-mvn
pom "help:effective-pom"
(lambda ()
- (let (xml-start-pos xml-end-pos)
- ;; find where we should start parsing XML
- (goto-char (point-min))
- (re-search-forward "<\\?xml\\|<projects?")
- (setq xml-start-pos (match-beginning 0))
- ;; determine the start tag
- (goto-char (point-min))
- (re-search-forward "<\\(projects?\\)")
- ;; find closing tag which is also the end of the region to parse
- (search-forward (concat "</" (match-string 1) ">"))
- (setq xml-end-pos (match-end 0))
- ;; parse
- (let ((artifact-pomfile-alist
- (javaimp-build-artifact-pomfile-alist (list pom)))
- (children (javaimp-get-projects
- (xml-parse-region xml-start-pos xml-end-pos))))
- (javaimp-maven-build-children children artifact-pomfile-alist))))))
-
-(defun javaimp-make-artifact-from-xml (node)
+ (let ((xml-start-pos
+ (save-excursion
+ (progn
+ (goto-char (point-min))
+ (re-search-forward "<\\?xml\\|<projects?")
+ (match-beginning 0))))
+ (xml-end-pos
+ (save-excursion
+ (progn
+ (goto-char (point-min))
+ (re-search-forward "<\\(projects?\\)")
+ ;; corresponding closing tag is the end of parse region
+ (search-forward (concat "</" (match-string 1) ">"))
+ (match-end 0)))))
+ (xml-parse-region xml-start-pos xml-end-pos)))))
+
+(defun javaimp-maven-load-module-tree (pom)
+ "Returns an alist of all Maven modules in a hierarchy starting
+with POM"
+ (let* ((effective-pom (javaimp-parse-effective-pom pom))
+ (project-elts
+ (cond ((assq 'projects effective-pom) ;project contains <module>
tag(s)
+ (javaimp-xml-child-list (assq 'projects effective-pom)
'project))
+ ((assq 'project effective-pom) ;single-module project
+ (list (assq 'project effective-pom)))
+ (t
+ (error "Cannot find projects in XML tree"))))
+ (modules-alist (javaimp-maven-process-projects project-elts)))
+ (javaimp-fill-pom-file-paths modules-alist pom)
+ modules-alist))
+
+(defun javaimp-fill-pom-file-paths (modules pom)
+ "Subroutine of `javaimp-maven-load-module-tree'"
+ (let ((artifact-alist (javaimp-traverse-pom-tree (list pom))))
+ (dolist (module modules-alist)
+ (let* ((artifact (javaimp-get-mod-artifact module))
+ (path
+ (cdr (or (seq-find (lambda (el)
+ (equal artifact (car el)))
+ artifact-alist)
+ ;; Compare just id if comparison by all fields failed
+ (seq-find (lambda (el)
+ (equal (javaimp-artifact-artifact-id
artifact)
+ (javaimp-artifact-artifact-id (car
el))))
+ artifact-alist)
+ (error "Cannot find path for artifact: %s" artifact)))))
+ (javaimp-set-mod-pom-file module path)))))
+
+(defun javaimp-extract-artifact-info (elt)
(javaimp-make-artifact
- (javaimp-xml-first-child (javaimp-xml-child 'groupId node))
- (javaimp-xml-first-child (javaimp-xml-child 'artifactId node))
- (javaimp-xml-first-child (javaimp-xml-child 'version node))))
-
-(defun javaimp-get-pom-file-path-lax (artifact artifact-pomfile-alist)
- (assoc-default
- artifact artifact-pomfile-alist
- (lambda (tested target)
- (or (equal target tested)
- (equal (javaimp-artifact-artifact-id target)
- (javaimp-artifact-artifact-id tested))))))
-
-(defun javaimp-maven-build-children (projects artifact-pomfile-alist)
- (let (result)
- (dolist (proj projects result)
- (let* ((artifact (javaimp-make-artifact-from-xml proj))
- (pom-file-path (javaimp-get-pom-file-path-lax
- artifact artifact-pomfile-alist))
- (build (javaimp-xml-child 'build proj))
- (source-dir (javaimp-xml-first-child
- (javaimp-xml-child 'sourceDirectory build)))
- (test-source-dir (javaimp-xml-first-child
- (javaimp-xml-child 'testSourceDirectory
- build)))
- (build-dir (javaimp-xml-first-child
- (javaimp-xml-child 'directory build)))
- (parent (javaimp-make-artifact-from-xml
- (javaimp-xml-child 'parent proj))))
- (push (javaimp-make-mod
- artifact
- pom-file-path
- (file-name-as-directory
- (if (eq system-type 'cygwin)
- (car (process-lines javaimp-cygpath-program "-u"
- source-dir))
- source-dir))
- (file-name-as-directory
- (if (eq system-type 'cygwin)
- (car (process-lines javaimp-cygpath-program "-u"
- test-source-dir))
- test-source-dir))
- (file-name-as-directory
- (if (eq system-type 'cygwin)
- (car (process-lines javaimp-cygpath-program "-u"
- build-dir))
- build-dir))
- nil nil parent nil)
- result)))))
-
-(defun javaimp-build-artifact-pomfile-alist (pom-file-list)
- "Recursively builds an alist where each element is of the
-form (\"ARTIFACT\" . \"POM-FILE-PATH\"). This is needed because
-there is no pom file path in the output of `mvn
-help:effective-pom'. Each pom file path in POM-FILE-LIST should
-be in platform's default format."
- (when pom-file-list
- (let ((pom-file (car pom-file-list))
- xml-tree project)
- (message "Saving artifact id -> pom file mapping for %s" pom-file)
- (with-temp-buffer
- (insert-file-contents pom-file)
- (setq xml-tree (xml-parse-region (point-min) (point-max))))
- (setq project (if (assq 'top xml-tree)
- (assq 'project (cddr (assq 'top xml-tree)))
- (assq 'project xml-tree)))
- (cons
- ;; this pom
- (cons (javaimp-make-artifact-from-xml project) pom-file)
- (append
- ;; submodules
- (javaimp-build-artifact-pomfile-alist
- (mapcar (lambda (submodule)
- (expand-file-name
- (concat
- ;; this pom's path
- (file-name-directory pom-file)
- ;; relative submodule directory
- (file-name-as-directory
- (let ((submodule-path (car (cddr submodule))))
- (if (eq system-type 'cygwin)
- (car (process-lines javaimp-cygpath-program "-u"
- submodule-path))
- submodule-path)))
- ;; well-known file name
- "pom.xml")))
- (javaimp-xml-child-list (assq 'modules (cddr project))
'module)))
- ;; rest items
- (javaimp-build-artifact-pomfile-alist (cdr pom-file-list)))))))
+ (javaimp-xml-first-child (javaimp-xml-child 'groupId elt))
+ (javaimp-xml-first-child (javaimp-xml-child 'artifactId elt))
+ (javaimp-xml-first-child (javaimp-xml-child 'version elt))))
+
+(defun javaimp-cygpath-convert-maybe (path)
+ (if (eq system-type 'cygwin)
+ (car (process-lines javaimp-cygpath-program "-u" source-dir))
+ source-dir))
+
+(defun javaimp-maven-process-projects (projects-elts)
+ (mapcar
+ (lambda (project-elt)
+ (let ((build-elt (javaimp-xml-child 'build project-elt)))
+ (javaimp-make-mod
+ (javaimp-extract-artifact-info project-elt)
+ nil ;pom-file will be set later
+ (file-name-as-directory
+ (javaimp-cygpath-convert-maybe
+ (javaimp-xml-first-child (javaimp-xml-child 'sourceDirectory
build-elt))))
+ (file-name-as-directory
+ (javaimp-cygpath-convert-maybe
+ (javaimp-xml-first-child (javaimp-xml-child 'testSourceDirectory
build-elt))))
+ (file-name-as-directory
+ (javaimp-cygpath-convert-maybe
+ (javaimp-xml-first-child (javaimp-xml-child 'directory build-elt))))
+ nil ;pom-file-mod-ts will be set later
+ nil ;jar-list will be set later
+ (javaimp-extract-artifact-info
+ (javaimp-xml-child 'parent project-elt))
+ nil ;parent-ts will be set later
+ )))
+ project-elts))
+
+(defun javaimp-extract-submodules-paths (project-elt)
+ (let* ((modules-elt (javaimp-xml-child 'modules project-elt))
+ (module-elts (javaimp-xml-child-list modules-elt 'module)))
+ (mapcar #'javaimp-xml-first-child module-elts)))
+
+(defun javaimp-parse-pom-file (pom-file)
+ "Subroutine of `javaimp-traverse-pom-tree'. Parses POM-FILE.
+Car of result is artifact info. Cdr of result is submodules
+relative path list."
+ (message "Parsing pom file mapping for %s" pom-file)
+ (let* ((xml-tree (with-temp-buffer
+ (insert-file-contents pom-file)
+ (xml-parse-region (point-min) (point-max))))
+ (project-elt (cond ((assq 'top xml-tree)
+ (javaimp-xml-child 'project (assq 'top xml-tree)))
+ ((assq 'project xml-tree)
+ (assq 'project xml-tree))
+ (t
+ (error "Cannot find <project> element in pom %s!"
pom-file)))))
+ (cons (javaimp-extract-artifact-info project-elt)
+ (javaimp-extract-submodules-paths project-elt))))
+
+(defun javaimp-create-absolute-submodules-paths (base-pom paths)
+ "Subroutine of `javaimp-traverse-pom-tree'"
+ (mapcar
+ (lambda (rel-path)
+ (expand-file-name
+ (concat
+ ;; base path
+ (file-name-directory base-pom)
+ ;; submodule relative path
+ (file-name-as-directory
+ (javaimp-cygpath-convert-maybe rel-path))
+ ;; well-known pom name
+ "pom.xml")))
+ paths))
+
+(defun javaimp-traverse-pom-tree (pom-file-list)
+ "Traverses pom tree and returns alist where each element is of
+the form (\"ARTIFACT\" . \"POM-FILE-PATH\"). Result paths are in
+platform default format."
+ (if pom-file-list
+ (append
+ ;; this item
+ (let* ((this-pom (car pom-file-list))
+ (pom-file-data (javaimp-parse-pom-file this-pom)))
+ (append
+ ;; this pom itself
+ (list (cons (car pom-file-data) this-pom))
+ ;; children of this pom
+ (javaimp-traverse-pom-tree
+ (javaimp-create-absolute-submodules-paths
+ this-pom (cdr pom-file-data)))))
+ ;; rest items
+ (javaimp-traverse-pom-tree (cdr pom-file-list)))))
(defun javaimp-call-mvn (pom-file target handler)
"Runs Maven target TARGET on POM-FILE, then calls HANDLER in
@@ -439,13 +469,14 @@ the temporary buffer and returns its result"
(with-current-buffer (get-buffer-create javaimp-debug-buf-name)
(erase-buffer)
(insert-buffer-substring output-buf))
- (unless (and (numberp status) (= status 0))
- (error "Maven target \"%s\" failed with status \"%s\""
- target status))
+ (or (and (numberp status) (= status 0))
+ (error "Maven target \"%s\" failed with status \"%s\""
+ target status))
+ (goto-char (point-min))
(funcall handler))))
-;;; Reading and caching dependencies
+;;; Working with module dependency JARs
(defun javaimp-maven-fetch-module-deps (module)
"Returns list of dependency jars for MODULE"
@@ -720,25 +751,30 @@ argument is a list of additional classes to import."
(javaimp-insert-import-groups static-import-groups t))
(message "Nothing to organize")))))
-;;;###autoload
(defun javaimp-invalidate-jar-classes-cache ()
"Resets jar classes cache (debugging only)"
(interactive)
(setq javaimp-jar-classes-cache nil))
-;;;###autoload
(defun javaimp-forget-all-visited-modules ()
"Resets `javaimp-maven-root-modules' (debugging only)"
(interactive)
(setq javaimp-maven-root-modules nil))
-;;;###autoload
(defun javaimp-reset ()
"Resets all data (debugging only)"
(interactive)
(javaimp-forget-all-visited-modules)
(javaimp-invalidate-jar-classes-cache))
+;; Some functions which can be used in other modules
+
+(defun javaimp-get-source-directories ()
+ (apply #'seq-concatenate 'list
+ (mapcar (lambda (root)
+ (mapcar #'javaimp-get-mod-source-dir (cdr root)))
+ javaimp-maven-root-modules)))
+
(provide 'javaimp)
;;; javaimp.el ends here