blob: 523c06ee325095c2a8dc4e7e9689ad4521f8bf0b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.felix.mosgi.jmx.agent.mx4j.loading;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.loading.MLet;
/**
* The parser for MLet files, as specified in the JMX documentation.
* This parser is case insensitive regards to the MLet tags: MLET is equal to mlet and to MLet.
* This parser also supports XML-style comments in the file.
*
* @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
* @version $Revision: 1.1.1.1 $
*/
public class MLetParser
{
public static final String OPEN_COMMENT = "<!--";
public static final String CLOSE_COMMENT = "-->";
public static final String OPEN_BRACKET = "<";
public static final String CLOSE_BRACKET = ">";
public static final String MLET_TAG = "MLET";
public static final String CODE_ATTR = "CODE";
public static final String OBJECT_ATTR = "OBJECT";
public static final String ARCHIVE_ATTR = "ARCHIVE";
public static final String CODEBASE_ATTR = "CODEBASE";
public static final String NAME_ATTR = "NAME";
public static final String VERSION_ATTR = "VERSION";
public static final String ARG_TAG = "ARG";
public static final String TYPE_ATTR = "TYPE";
public static final String VALUE_ATTR = "VALUE";
private MLet mlet;
/**
* Creates a new MLetParser
*/
public MLetParser()
{
}
/**
* Creates a new MLetParser
* @param mlet The MLet used to resolve classes specified in the ARG tags.
*/
public MLetParser(MLet mlet)
{
this.mlet = mlet;
}
/**
* Parses the given content, that must contains a valid MLet file.
*
* @param content The content to parse
* @return A list of {@link MLetTag}s
* @throws MLetParseException If the content is not a valid MLet file
*/
public List parse(String content) throws MLetParseException
{
if (content == null) throw new MLetParseException("MLet file content cannot be null");
// Strip comments
content = stripComments(content.trim());
content = convertToUpperCase(content);
ArrayList mlets = parseMLets(content);
if (mlets.size() < 1) throw new MLetParseException("MLet file is empty");
ArrayList mletTags = new ArrayList();
for (int i = 0; i < mlets.size(); ++i)
{
String mletTag = (String)mlets.get(i);
MLetTag tag = parseMLet(mletTag);
mletTags.add(tag);
}
return mletTags;
}
private MLetTag parseMLet(String content) throws MLetParseException
{
MLetTag tag = new MLetTag();
parseMLetAttributes(tag, content);
parseMLetArguments(tag, content);
return tag;
}
private ArrayList parseMLets(String content) throws MLetParseException
{
ArrayList list = new ArrayList();
int start = 0;
int current = -1;
while ((current = findOpenTag(content, start, MLET_TAG)) >= 0)
{
int end = findCloseTag(content, current + 1, MLET_TAG, true);
if (end < 0) throw new MLetParseException("MLET tag not closed at index: " + current);
String mlet = content.substring(current, end);
list.add(mlet);
start = end + 1;
}
return list;
}
private void parseMLetArguments(MLetTag tag, String content) throws MLetParseException
{
int start = 0;
int current = -1;
while ((current = findOpenTag(content, start, ARG_TAG)) >= 0)
{
int end = findCloseTag(content, current + 1, ARG_TAG, false);
if (end < 0) throw new MLetParseException("ARG tag not closed");
String arg = content.substring(current, end);
int type = arg.indexOf(TYPE_ATTR);
if (type < 0) throw new MLetParseException("Missing TYPE attribute");
int value = arg.indexOf(VALUE_ATTR);
if (value < 0) throw new MLetParseException("Missing VALUE attribute");
String className = findAttributeValue(arg, type, TYPE_ATTR);
tag.addArg(className, convertToObject(className, findAttributeValue(arg, value, VALUE_ATTR)));
start = end + 1;
}
}
private void parseMLetAttributes(MLetTag tag, String content) throws MLetParseException
{
int end = content.indexOf(CLOSE_BRACKET);
String attributes = content.substring(0, end);
// Find mandatory attributes
int archive = -1;
int object = -1;
int code = -1;
archive = attributes.indexOf(ARCHIVE_ATTR);
if (archive < 0) throw new MLetParseException("Missing ARCHIVE attribute");
code = attributes.indexOf(CODE_ATTR);
object = attributes.indexOf(OBJECT_ATTR);
if (code < 0 && object < 0) throw new MLetParseException("Missing CODE or OBJECT attribute");
if (code > 0 && object > 0) throw new MLetParseException("CODE and OBJECT attributes cannot be both present");
if (code >= 0)
tag.setCode(findAttributeValue(attributes, code, CODE_ATTR));
else
tag.setObject(findAttributeValue(attributes, object, OBJECT_ATTR));
tag.setArchive(findAttributeValue(attributes, archive, ARCHIVE_ATTR));
// Look for optional attributes
int codebase = attributes.indexOf(CODEBASE_ATTR);
if (codebase >= 0) tag.setCodeBase(findAttributeValue(attributes, codebase, CODEBASE_ATTR));
int name = attributes.indexOf(NAME_ATTR);
if (name >= 0)
{
String objectName = findAttributeValue(attributes, name, NAME_ATTR);
try
{
tag.setName(new ObjectName(objectName));
}
catch (MalformedObjectNameException x)
{
throw new MLetParseException("Invalid ObjectName: " + objectName);
}
}
int version = attributes.indexOf(VERSION_ATTR);
if (version >= 0) tag.setVersion(findAttributeValue(attributes, version, VERSION_ATTR));
}
private String findAttributeValue(String content, int start, String attribute) throws MLetParseException
{
int equal = content.indexOf('=', start);
if (equal < 0) throw new MLetParseException("Missing '=' for attribute");
// Ensure no garbage
if (!attribute.equals(content.substring(start, equal).trim())) throw new MLetParseException("Invalid attribute");
int begin = content.indexOf('"', equal + 1);
if (begin < 0) throw new MLetParseException("Missing quotes for attribute value");
// Ensure no garbage
if (content.substring(equal + 1, begin).trim().length() != 0) throw new MLetParseException("Invalid attribute value");
int end = content.indexOf('"', begin + 1);
if (end < 0) throw new MLetParseException("Missing quote for attribute value");
return content.substring(begin + 1, end).trim();
}
private int findOpenTag(String content, int start, String tag)
{
String opening = new StringBuffer(OPEN_BRACKET).append(tag).toString();
return content.indexOf(opening, start);
}
private int findCloseTag(String content, int start, String tag, boolean strictSyntax)
{
int count = 1;
do
{
int close = content.indexOf(CLOSE_BRACKET, start);
if (close < 0)
{
return -1;
}
int open = content.indexOf(OPEN_BRACKET, start);
if (open >= 0 && close > open)
{
++count;
}
else
{
--count;
if (count == 0)
{
// Either I found the closing bracket of the open tag,
// or the closing tag
if (!strictSyntax || (strictSyntax && content.charAt(close - 1) == '/'))
{
// Found the closing tag
return close + 1;
}
else
{
// Found the closing bracket of the open tag, go for the full closing tag
String closing = new StringBuffer(OPEN_BRACKET).append("/").append(tag).append(CLOSE_BRACKET).toString();
close = content.indexOf(closing, start);
if (close < 0)
return -1;
else
return close + closing.length();
}
}
}
start = close + 1;
}
while (true);
}
private String stripComments(String content) throws MLetParseException
{
StringBuffer buffer = new StringBuffer();
int start = 0;
int current = -1;
while ((current = content.indexOf(OPEN_COMMENT, start)) >= 0)
{
int end = content.indexOf(CLOSE_COMMENT, current + 1);
if (end < 0) throw new MLetParseException("Missing close comment tag at index: " + current);
String stripped = content.substring(start, current);
buffer.append(stripped);
start = end + CLOSE_COMMENT.length();
}
String stripped = content.substring(start, content.length());
buffer.append(stripped);
return buffer.toString();
}
private String convertToUpperCase(String content) throws MLetParseException
{
StringBuffer buffer = new StringBuffer();
int start = 0;
int current = -1;
while ((current = content.indexOf("\"", start)) >= 0)
{
int end = content.indexOf("\"", current + 1);
if (end < 0) throw new MLetParseException("Missing closing quote at index: " + current);
String converted = content.substring(start, current).toUpperCase();
buffer.append(converted);
String quoted = content.substring(current, end + 1);
buffer.append(quoted);
start = end + 1;
}
String converted = content.substring(start, content.length()).toUpperCase();
buffer.append(converted);
return buffer.toString();
}
private Object convertToObject(String clsName, String value) throws MLetParseException
{
try
{
if (clsName.equals("boolean") || clsName.equals("java.lang.Boolean"))
return Boolean.valueOf(value);
else if (clsName.equals("byte") || clsName.equals("java.lang.Byte"))
return Byte.valueOf(value);
else if (clsName.equals("char") || clsName.equals("java.lang.Character"))
{
char ch = 0;
if (value.length() > 0) ch = value.charAt(0);
return new Character(ch);
}
else if (clsName.equals("short") || clsName.equals("java.lang.Short"))
return Short.valueOf(value);
else if (clsName.equals("int") || clsName.equals("java.lang.Integer"))
return Integer.valueOf(value);
else if (clsName.equals("long") || clsName.equals("java.lang.Long"))
return Long.valueOf(value);
else if (clsName.equals("float") || clsName.equals("java.lang.Float"))
return Float.valueOf(value);
else if (clsName.equals("double") || clsName.equals("java.lang.Double"))
return Double.valueOf(value);
else if (clsName.equals("java.lang.String"))
return value;
else if (mlet != null)
{
try
{
Class cls = mlet.loadClass(clsName);
Constructor ctor = cls.getConstructor(new Class[]{String.class});
return ctor.newInstance(new Object[]{value});
}
catch (Exception ignored)
{
}
}
}
catch (NumberFormatException x)
{
throw new MLetParseException("Invalid value: " + value);
}
return null;
}
}