View Javadoc
1   /*
2    * This file is part of dependency-check-core.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   * Copyright (c) 2015 Jeremy Long. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.xml.pom;
19  
20  import java.util.ArrayDeque;
21  import java.util.Deque;
22  import javax.annotation.concurrent.NotThreadSafe;
23  import org.xml.sax.Attributes;
24  import org.xml.sax.SAXException;
25  import org.xml.sax.helpers.DefaultHandler;
26  
27  /**
28   * A handler to read the pom.xml model.
29   *
30   * @author Jeremy Long
31   */
32  @NotThreadSafe
33  public class PomHandler extends DefaultHandler {
34  
35      /**
36       * The project element.
37       */
38      public static final String PROJECT = "project";
39      /**
40       * The artifactId element.
41       */
42      public static final String GROUPID = "groupId";
43      /**
44       * The artifactId element.
45       */
46      public static final String ARTIFACTID = "artifactId";
47      /**
48       * The version element.
49       */
50      public static final String VERSION = "version";
51      /**
52       * The parent element.
53       */
54      public static final String PARENT = "parent";
55      /**
56       * The name element.
57       */
58      public static final String NAME = "name";
59      /**
60       * The organization element.
61       */
62      public static final String ORGANIZATION = "organization";
63      /**
64       * The description element.
65       */
66      public static final String DESCRIPTION = "description";
67      /**
68       * The licenses element.
69       */
70      public static final String LICENSES = "licenses";
71      /**
72       * The license element.
73       */
74      public static final String LICENSE_NODE = "license";
75      /**
76       * The developers element.
77       */
78      public static final String DEVELOPERS = "developers";
79      /**
80       * The developer element.
81       */
82      public static final String DEVELOPER_NODE = "developer";
83      /**
84       * The developer id element.
85       */
86      public static final String DEVELOPER_ID = "id";
87      /**
88       * The developer email element.
89       */
90      public static final String DEVELOPER_EMAIL = "email";
91      /**
92       * The developer organization element.
93       */
94      public static final String DEVELOPER_ORGANIZATION = "organization";
95      /**
96       * The developer organization URL element.
97       */
98      public static final String DEVELOPER_ORGANIZATION_URL = "organizationUrl";
99      /**
100      * The URL element.
101      */
102     public static final String URL = "url";
103     /**
104      * The pom model.
105      */
106     private final Model model = new Model();
107     /**
108      * The stack of elements processed; used to determine the parent node.
109      */
110     private final Deque<String> stack = new ArrayDeque<>();
111     /**
112      * The license object.
113      */
114     private License license = null;
115     /**
116      * The developer object.
117      */
118     private Developer developer = null;
119     /**
120      * The current node text being extracted from the element.
121      */
122     private StringBuilder currentText;
123 
124     /**
125      * Returns the model obtained from the pom.xml.
126      *
127      * @return the model object
128      */
129     public Model getModel() {
130         return model;
131     }
132 
133     /**
134      * Handles the start element event.
135      *
136      * @param uri the uri of the element being processed
137      * @param localName the local name of the element being processed
138      * @param qName the qName of the element being processed
139      * @param attributes the attributes of the element being processed
140      * @throws SAXException thrown if there is an exception processing
141      */
142     @Override
143     public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
144         currentText = new StringBuilder();
145         stack.push(qName);
146         if (LICENSE_NODE.equals(qName)) {
147             license = new License();
148         } else if (DEVELOPER_NODE.equals(qName)) {
149             developer = new Developer();
150         }
151     }
152 
153     /**
154      * Handles the end element event.
155      *
156      * @param uri the URI of the element
157      * @param localName the local name of the element
158      * @param qName the qName of the element
159      * @throws SAXException thrown if there is an exception processing
160      */
161     @Override
162     public void endElement(String uri, String localName, String qName) throws SAXException {
163         stack.pop();
164         final String parentNode = stack.peek();
165         if (null != parentNode) {
166             switch (parentNode) {
167                 case PROJECT:
168                     if (null != qName) {
169                         switch (qName) {
170                             case GROUPID:
171                                 model.setGroupId(currentText.toString());
172                                 break;
173                             case ARTIFACTID:
174                                 model.setArtifactId(currentText.toString());
175                                 break;
176                             case VERSION:
177                                 model.setVersion(currentText.toString().trim());
178                                 break;
179                             case NAME:
180                                 model.setName(currentText.toString());
181                                 break;
182                             case DESCRIPTION:
183                                 model.setDescription(currentText.toString());
184                                 break;
185                             case URL:
186                                 model.setProjectURL(currentText.toString());
187                                 break;
188                             default:
189                                 break;
190                         }
191                     }
192                     break;
193                 case ORGANIZATION:
194                     if (NAME.equals(qName)) {
195                         model.setOrganization(currentText.toString());
196                     } else if (URL.equals(qName)) {
197                         model.setOrganizationUrl(currentText.toString());
198                     }
199                     break;
200                 case PARENT:
201                     if (null != qName) {
202                         switch (qName) {
203                             case GROUPID:
204                                 model.setParentGroupId(currentText.toString());
205                                 break;
206                             case ARTIFACTID:
207                                 model.setParentArtifactId(currentText.toString());
208                                 break;
209                             case VERSION:
210                                 model.setParentVersion(currentText.toString());
211                                 break;
212                             default:
213                                 break;
214                         }
215                     }
216                     break;
217                 case LICENSE_NODE:
218                     if (license != null) {
219                         if (NAME.equals(qName)) {
220                             license.setName(currentText.toString());
221                         } else if (URL.equals(qName)) {
222                             license.setUrl(currentText.toString());
223                         }
224                     }
225                     break;
226                 case LICENSES:
227                     if (LICENSE_NODE.equals(qName) && license != null) {
228                         model.addLicense(license);
229                         license = null;
230                     }
231                     break;
232                 case DEVELOPER_NODE:
233                     if (developer != null && qName != null) {
234                         switch (qName) {
235                             case DEVELOPER_ID:
236                                 developer.setId(currentText.toString());
237                                 break;
238                             case NAME:
239                                 developer.setName(currentText.toString());
240                                 break;
241                             case DEVELOPER_EMAIL:
242                                 developer.setEmail(currentText.toString());
243                                 break;
244                             case DEVELOPER_ORGANIZATION:
245                                 developer.setOrganization(currentText.toString());
246                                 break;
247                             case DEVELOPER_ORGANIZATION_URL:
248                                 developer.setOrganizationUrl(currentText.toString());
249                                 break;
250                             default:
251                                 break;
252                         }
253                     }
254                     break;
255                 case DEVELOPERS:
256                     if (DEVELOPER_NODE.equals(qName) && developer != null) {
257                         model.addDeveloper(developer);
258                         developer = null;
259                     }
260                     break;
261                 default:
262                     break;
263             }
264         }
265     }
266 
267     /**
268      * Collects the body text of the node being processed.
269      *
270      * @param ch the char array of text
271      * @param start the start position to copy text from in the char array
272      * @param length the number of characters to copy from the char array
273      * @throws SAXException thrown if there is a parsing exception
274      */
275     @Override
276     public void characters(char[] ch, int start, int length) throws SAXException {
277         currentText.append(ch, start, length);
278     }
279 }