001: /*
002: * Copyright (C) The MX4J Contributors.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the MX4J License version 1.0.
006: * See the terms of the MX4J License in the documentation provided with this software.
007: */
008:
009: package mx4j.loading;
010:
011: import java.lang.reflect.Constructor;
012: import java.util.ArrayList;
013: import java.util.List;
014: import javax.management.MalformedObjectNameException;
015: import javax.management.ObjectName;
016: import javax.management.loading.MLet;
017:
018: /**
019: * The parser for MLet files, as specified in the JMX documentation.
020: * This parser is case insensitive regards to the MLet tags: MLET is equal to mlet and to MLet.
021: * This parser also supports XML-style comments in the file.
022: *
023: * @version $Revision: 1.6 $
024: */
025: public class MLetParser {
026: public static final String OPEN_COMMENT = "<!--";
027: public static final String CLOSE_COMMENT = "-->";
028:
029: public static final String OPEN_BRACKET = "<";
030: public static final String CLOSE_BRACKET = ">";
031:
032: public static final String MLET_TAG = "MLET";
033: public static final String CODE_ATTR = "CODE";
034: public static final String OBJECT_ATTR = "OBJECT";
035: public static final String ARCHIVE_ATTR = "ARCHIVE";
036: public static final String CODEBASE_ATTR = "CODEBASE";
037: public static final String NAME_ATTR = "NAME";
038: public static final String VERSION_ATTR = "VERSION";
039:
040: public static final String ARG_TAG = "ARG";
041: public static final String TYPE_ATTR = "TYPE";
042: public static final String VALUE_ATTR = "VALUE";
043:
044: private MLet mlet;
045:
046: /**
047: * Creates a new MLetParser
048: */
049: public MLetParser() {
050: }
051:
052: /**
053: * Creates a new MLetParser
054: *
055: * @param mlet The MLet used to resolve classes specified in the ARG tags.
056: */
057: public MLetParser(MLet mlet) {
058: this .mlet = mlet;
059: }
060:
061: /**
062: * Parses the given content, that must contains a valid MLet file.
063: *
064: * @param content The content to parse
065: * @return A list of {@link MLetTag}s
066: * @throws MLetParseException If the content is not a valid MLet file
067: */
068: public List parse(String content) throws MLetParseException {
069: if (content == null)
070: throw new MLetParseException(
071: "MLet file content cannot be null");
072:
073: // Strip comments
074: content = stripComments(content.trim());
075: content = convertToUpperCase(content);
076:
077: ArrayList mlets = parseMLets(content);
078: if (mlets.size() < 1)
079: throw new MLetParseException("MLet file is empty");
080:
081: ArrayList mletTags = new ArrayList();
082: for (int i = 0; i < mlets.size(); ++i) {
083: String mletTag = (String) mlets.get(i);
084:
085: MLetTag tag = parseMLet(mletTag);
086: mletTags.add(tag);
087: }
088:
089: return mletTags;
090: }
091:
092: private MLetTag parseMLet(String content) throws MLetParseException {
093: MLetTag tag = new MLetTag();
094: parseMLetAttributes(tag, content);
095: parseMLetArguments(tag, content);
096: return tag;
097: }
098:
099: private ArrayList parseMLets(String content)
100: throws MLetParseException {
101: ArrayList list = new ArrayList();
102: int start = 0;
103: int current = -1;
104: while ((current = findOpenTag(content, start, MLET_TAG)) >= 0) {
105: int end = findCloseTag(content, current + 1, MLET_TAG, true);
106: if (end < 0)
107: throw new MLetParseException(
108: "MLET tag not closed at index: " + current);
109:
110: String mlet = content.substring(current, end);
111: list.add(mlet);
112:
113: start = end + 1;
114: }
115: return list;
116: }
117:
118: private void parseMLetArguments(MLetTag tag, String content)
119: throws MLetParseException {
120: int start = 0;
121: int current = -1;
122: while ((current = findOpenTag(content, start, ARG_TAG)) >= 0) {
123: int end = findCloseTag(content, current + 1, ARG_TAG, false);
124: if (end < 0)
125: throw new MLetParseException("ARG tag not closed");
126:
127: String arg = content.substring(current, end);
128:
129: int type = arg.indexOf(TYPE_ATTR);
130: if (type < 0)
131: throw new MLetParseException("Missing TYPE attribute");
132:
133: int value = arg.indexOf(VALUE_ATTR);
134: if (value < 0)
135: throw new MLetParseException("Missing VALUE attribute");
136:
137: String className = findAttributeValue(arg, type, TYPE_ATTR);
138: tag.addArg(className, convertToObject(className,
139: findAttributeValue(arg, value, VALUE_ATTR)));
140:
141: start = end + 1;
142: }
143: }
144:
145: private void parseMLetAttributes(MLetTag tag, String content)
146: throws MLetParseException {
147: int end = content.indexOf(CLOSE_BRACKET);
148: String attributes = content.substring(0, end);
149:
150: // Find mandatory attributes
151: int archive = -1;
152: int object = -1;
153: int code = -1;
154:
155: archive = attributes.indexOf(ARCHIVE_ATTR);
156: if (archive < 0)
157: throw new MLetParseException("Missing ARCHIVE attribute");
158:
159: int start = 0;
160: do {
161: code = attributes.indexOf(CODE_ATTR, start);
162: start = code + 4;
163: } while (code != -1 && attributes.charAt(code + 4) == 'B');
164: object = attributes.indexOf(OBJECT_ATTR);
165: if (code < 0 && object < 0)
166: throw new MLetParseException(
167: "Missing CODE or OBJECT attribute");
168: if (code > 0 && object > 0)
169: throw new MLetParseException(
170: "CODE and OBJECT attributes cannot be both present");
171:
172: if (code >= 0) {
173: String codeAttr = findAttributeValue(attributes, code,
174: CODE_ATTR);
175: if (codeAttr.endsWith(".class")) {
176: codeAttr = codeAttr.substring(0, codeAttr.length() - 6);
177: }
178: tag.setCode(codeAttr);
179: } else
180: tag.setObject(findAttributeValue(attributes, object,
181: OBJECT_ATTR));
182:
183: tag.setArchive(findAttributeValue(attributes, archive,
184: ARCHIVE_ATTR));
185:
186: // Look for optional attributes
187: int codebase = attributes.indexOf(CODEBASE_ATTR);
188: if (codebase >= 0)
189: tag.setCodeBase(findAttributeValue(attributes, codebase,
190: CODEBASE_ATTR));
191:
192: int name = attributes.indexOf(NAME_ATTR);
193: if (name >= 0) {
194: String objectName = findAttributeValue(attributes, name,
195: NAME_ATTR);
196: try {
197: tag.setName(new ObjectName(objectName));
198: } catch (MalformedObjectNameException x) {
199: throw new MLetParseException("Invalid ObjectName: "
200: + objectName);
201: }
202: }
203:
204: int version = attributes.indexOf(VERSION_ATTR);
205: if (version >= 0)
206: tag.setVersion(findAttributeValue(attributes, version,
207: VERSION_ATTR));
208: }
209:
210: private String findAttributeValue(String content, int start,
211: String attribute) throws MLetParseException {
212: int equal = content.indexOf('=', start);
213: if (equal < 0)
214: throw new MLetParseException("Missing '=' for attribute");
215:
216: // Ensure no garbage
217: if (!attribute.equals(content.substring(start, equal).trim()))
218: throw new MLetParseException("Invalid attribute");
219:
220: int begin = content.indexOf('"', equal + 1);
221: String value;
222: if (begin == -1) {
223: int end = equal;
224: while (++end < content.length()) {
225: char ch = content.charAt(end);
226: if (ch == '\r' || ch == '\n' || ch == '>')
227: break;
228: }
229: if (end == content.length())
230: throw new MLetParseException(
231: "Missing end for value of attribute "
232: + attribute);
233: value = content.substring(equal + 1, end);
234: } else {
235: int end = content.indexOf('"', begin + 1);
236: if (end == -1)
237: throw new MLetParseException(
238: "Missing closing \" for value of attribute "
239: + attribute);
240: value = content.substring(begin + 1, end);
241: }
242: value = value.trim();
243: if (value.length() == 0)
244: throw new MLetParseException("Invalid attribute value");
245:
246: return value;
247: }
248:
249: private int findOpenTag(String content, int start, String tag) {
250: String opening = new StringBuffer(OPEN_BRACKET).append(tag)
251: .toString();
252: return content.indexOf(opening, start);
253: }
254:
255: private int findCloseTag(String content, int start, String tag,
256: boolean strictSyntax) {
257: int count = 1;
258:
259: do {
260: int close = content.indexOf(CLOSE_BRACKET, start);
261: if (close < 0) {
262: return -1;
263: }
264: int open = content.indexOf(OPEN_BRACKET, start);
265: if (open >= 0 && close > open) {
266: ++count;
267: } else {
268: --count;
269: if (count == 0) {
270: // Either I found the closing bracket of the open tag,
271: // or the closing tag
272: if (!strictSyntax
273: || (strictSyntax && content
274: .charAt(close - 1) == '/')) {
275: // Found the closing tag
276: return close + 1;
277: } else {
278: // Found the closing bracket of the open tag, go for the full closing tag
279: String closing = new StringBuffer(OPEN_BRACKET)
280: .append("/").append(tag).append(
281: CLOSE_BRACKET).toString();
282: close = content.indexOf(closing, start);
283: if (close < 0)
284: return -1;
285: else
286: return close + closing.length();
287: }
288: }
289: }
290:
291: start = close + 1;
292: } while (true);
293: }
294:
295: private String stripComments(String content)
296: throws MLetParseException {
297: StringBuffer buffer = new StringBuffer();
298: int start = 0;
299: int current = -1;
300: while ((current = content.indexOf(OPEN_COMMENT, start)) >= 0) {
301: int end = content.indexOf(CLOSE_COMMENT, current + 1);
302:
303: if (end < 0)
304: throw new MLetParseException(
305: "Missing close comment tag at index: "
306: + current);
307:
308: String stripped = content.substring(start, current);
309: buffer.append(stripped);
310: start = end + CLOSE_COMMENT.length();
311: }
312: String stripped = content.substring(start, content.length());
313: buffer.append(stripped);
314: return buffer.toString();
315: }
316:
317: private String convertToUpperCase(String content)
318: throws MLetParseException {
319: StringBuffer buffer = new StringBuffer();
320: int start = 0;
321: int current = -1;
322: while ((current = content.indexOf("\"", start)) >= 0) {
323: int end = content.indexOf("\"", current + 1);
324:
325: if (end < 0)
326: throw new MLetParseException(
327: "Missing closing quote at index: " + current);
328:
329: String converted = content.substring(start, current)
330: .toUpperCase();
331: buffer.append(converted);
332: String quoted = content.substring(current, end + 1);
333: buffer.append(quoted);
334: start = end + 1;
335: }
336: String converted = content.substring(start, content.length())
337: .toUpperCase();
338: buffer.append(converted);
339: return buffer.toString();
340: }
341:
342: private Object convertToObject(String clsName, String value)
343: throws MLetParseException {
344: try {
345: if (clsName.equals("boolean")
346: || clsName.equals("java.lang.Boolean"))
347: return Boolean.valueOf(value);
348: else if (clsName.equals("byte")
349: || clsName.equals("java.lang.Byte"))
350: return Byte.valueOf(value);
351: else if (clsName.equals("char")
352: || clsName.equals("java.lang.Character")) {
353: char ch = 0;
354: if (value.length() > 0)
355: ch = value.charAt(0);
356: return new Character(ch);
357: } else if (clsName.equals("short")
358: || clsName.equals("java.lang.Short"))
359: return Short.valueOf(value);
360: else if (clsName.equals("int")
361: || clsName.equals("java.lang.Integer"))
362: return Integer.valueOf(value);
363: else if (clsName.equals("long")
364: || clsName.equals("java.lang.Long"))
365: return Long.valueOf(value);
366: else if (clsName.equals("float")
367: || clsName.equals("java.lang.Float"))
368: return Float.valueOf(value);
369: else if (clsName.equals("double")
370: || clsName.equals("java.lang.Double"))
371: return Double.valueOf(value);
372: else if (clsName.equals("java.lang.String"))
373: return value;
374: else if (mlet != null) {
375: try {
376: Class cls = mlet.loadClass(clsName);
377: Constructor ctor = cls
378: .getConstructor(new Class[] { String.class });
379: return ctor.newInstance(new Object[] { value });
380: } catch (Exception ignored) {
381: }
382: }
383: } catch (NumberFormatException x) {
384: throw new MLetParseException("Invalid value: " + value);
385: }
386: return null;
387: }
388: }
|