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 }