001: package org.acm.seguin.pmd;
002:
003: import org.acm.seguin.pmd.util.ResourceLoader;
004: import org.xml.sax.Attributes;
005: import org.xml.sax.InputSource;
006: import org.xml.sax.SAXException;
007: import org.xml.sax.helpers.DefaultHandler;
008:
009: import javax.xml.parsers.SAXParser;
010: import javax.xml.parsers.SAXParserFactory;
011: import java.io.InputStream;
012: import java.util.Enumeration;
013: import java.util.Iterator;
014: import java.util.Properties;
015: import java.util.Stack;
016: import java.util.StringTokenizer;
017:
018: /**
019: * Loads the PMD <b>project.xml</b> file and stores the contents in a Properties object.
020: * The property keys are the case-insensitive path starting below the root <project> down
021: * to the element. For example:
022: * <ul>
023: * <li>currentVersion</li>
024: * <li>organization/name</li>
025: * <li>versions/version/name</li>
026: * </ul>
027: * When an element has repeated values, e.g., developer names, one property is created and
028: * the values are separated by <b>&vs;</b>. The <i>vs</i> stands for <i>value separator</i>.
029: * For example:
030: * <ul>
031: * <li>Tom Copeland&vs;David Dixon-Peugh&vs;David Craine</li>
032: * <li>tom@infoether.com&vs;ddp@apache.org&vs;dave@infoether.com</li>
033: * </ul>
034: * When there is a collection of repeating values, an empty space will reserve the position
035: * of a missing value. This is so that the collection can be parsed on position.
036: *
037: * @author Donald A. Leckie
038: * @since September 10, 2002
039: * @version $Revision: 1.1 $, $Date: 2003/07/29 20:51:58 $
040: */
041: public class ProjectFile {
042:
043: private static Properties PROPERTIES;
044: private static Exception PARSE_EXCEPTION;
045: private static final String VALUE_SEPARATOR = "&vs;";
046:
047: /**
048: *****************************************************************************
049: *
050: * @param key
051: *
052: * @return
053: */
054: public static final String getProperty(String key) {
055: key = (key == null) ? "" : key.trim().toLowerCase();
056:
057: if (PROPERTIES == null) {
058: (new ProjectFile()).loadProperties();
059: }
060:
061: String value = PROPERTIES.getProperty(key);
062:
063: return (value == null) ? "" : value;
064: }
065:
066: /**
067: *****************************************************************************
068: *
069: * @return
070: */
071: public static final Enumeration getPropertyKeys() {
072: return PROPERTIES.propertyNames();
073: }
074:
075: /**
076: *****************************************************************************
077: *
078: * @return
079: */
080: public static final int getPropertyCount() {
081: int count = 0;
082: Enumeration keys = PROPERTIES.propertyNames();
083:
084: while (keys.hasMoreElements()) {
085: keys.nextElement();
086: count++;
087: }
088:
089: return count;
090: }
091:
092: /**
093: *****************************************************************************
094: *
095: * @return
096: */
097: public static final String[] toArray(String propertyValue) {
098: String[] values = new String[0];
099:
100: if (propertyValue != null) {
101: StringTokenizer parser;
102: int valueCount;
103: int index;
104:
105: parser = new StringTokenizer(propertyValue, VALUE_SEPARATOR);
106: valueCount = parser.countTokens();
107: values = new String[valueCount];
108: index = 0;
109:
110: while (parser.hasMoreTokens()) {
111: values[index] = parser.nextToken();
112: index++;
113: }
114: }
115:
116: return values;
117: }
118:
119: /**
120: *****************************************************************************
121: *
122: * @return
123: */
124: public static final Exception getException() {
125: return PARSE_EXCEPTION;
126: }
127:
128: /**
129: *****************************************************************************
130: *
131: */
132: private void loadProperties() {
133: InputStream inputStream;
134: InputSource inputSource;
135:
136: PROPERTIES = new Properties();
137:
138: try {
139: inputStream = ResourceLoader
140: .loadResourceAsStream("project.xml");
141: inputSource = new InputSource(inputStream);
142: MainContentHandler mainContentHandler;
143: SAXParserFactory factory = SAXParserFactory.newInstance();
144: factory.setFeature(
145: "http://xml.org/sax/features/namespace-prefixes",
146: true);
147: factory.setFeature(
148: "http://xml.org/sax/features/namespaces", false);
149:
150: SAXParser parser = factory.newSAXParser();
151:
152: mainContentHandler = new MainContentHandler();
153:
154: parser.parse(inputSource, mainContentHandler);
155: } catch (Exception exception) {
156: PARSE_EXCEPTION = exception;
157: }
158: }
159:
160: /**
161: *****************************************************************************
162: *****************************************************************************
163: *****************************************************************************
164: */
165: private class MainContentHandler extends DefaultHandler {
166:
167: private StringBuffer m_buffer = new StringBuffer(100);
168: private Stack m_nameStack = new Stack();
169: private final String PROJECT = "project";
170:
171: /**
172: *************************************************************************
173: */
174: private MainContentHandler() {
175: super ();
176: }
177:
178: /**
179: *************************************************************************
180: *
181: * @param namespace
182: * @param localName
183: * @param qualifiedName
184: * @param attributes
185: *
186: * @throws SAXException
187: */
188: public void startElement(String namespace, String localName,
189: String qualifiedName, Attributes attributes)
190: throws SAXException {
191: if (qualifiedName.equalsIgnoreCase(PROJECT) == false) {
192: m_nameStack.push(qualifiedName);
193: }
194: }
195:
196: /**
197: *************************************************************************
198: *
199: * @param chars
200: * @param beginIndex
201: * @param length
202: *
203: * @throws PMDException
204: */
205: public void characters(char[] chars, int beginIndex, int length) {
206: m_buffer.append(chars, beginIndex, length);
207: }
208:
209: /**
210: *************************************************************************
211: *
212: * @param namespace
213: * @param localName
214: * @param qualifiedName
215: *
216: * @throws SAXException
217: */
218: public void endElement(String namespace, String localName,
219: String qualifiedName) throws SAXException {
220: String value = m_buffer.toString().replace('\n', ' ')
221: .trim();
222: String key = buildKey();
223: String existingValue = PROPERTIES.getProperty(key);
224:
225: if (existingValue != null) {
226: value = existingValue + VALUE_SEPARATOR + value;
227: }
228:
229: PROPERTIES.setProperty(key, value);
230: m_buffer.setLength(0);
231: m_nameStack.pop();
232: }
233:
234: /**
235: *************************************************************************
236: *
237: * @return
238: */
239: private String buildKey() {
240: StringBuffer name = new StringBuffer(100);
241: Iterator iterator = m_nameStack.iterator();
242:
243: while (iterator.hasNext()) {
244: name.append(iterator.next());
245: name.append('/');
246: }
247:
248: if (name.length() > 0) {
249: name.setLength(name.length() - 1);
250: }
251:
252: return name.toString().toLowerCase();
253: }
254: }
255: }
|