blob: 0056d65fa66a398ec699095e00b0d8e5017bf13b [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.
*/
options {
STATIC = false;
}
PARSER_BEGIN(PropertyListParser)
package org.apache.commons.configuration2.plist;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import org.apache.commons.configuration2.HierarchicalConfiguration;
import org.apache.commons.configuration2.tree.ImmutableNode;
import org.apache.commons.codec.binary.Hex;
/**
* JavaCC based parser for the PropertyList format.
*
* @author Emmanuel Bourg
* @version $Id$
*/
class PropertyListParser {
/**
* Remove the quotes at the beginning and at the end of the specified String.
*/
protected String removeQuotes(String s)
{
if (s == null)
{
return null;
}
if (s.startsWith("\"") && s.endsWith("\"") && s.length() >= 2)
{
s = s.substring(1, s.length() - 1);
}
return s;
}
protected String unescapeQuotes(String s)
{
return s.replaceAll("\\\\\"", "\"");
}
/**
* Remove the white spaces and the data delimiters from the specified
* string and parse it as a byte array.
*/
protected byte[] filterData(String s) throws ParseException
{
if (s == null)
{
return null;
}
// remove the delimiters
if (s.startsWith("<") && s.endsWith(">") && s.length() >= 2)
{
s = s.substring(1, s.length() - 1);
}
// remove the white spaces
s = s.replaceAll("\\s", "");
// add a leading 0 to ensure well formed bytes
if (s.length() % 2 != 0)
{
s = "0" + s;
}
// parse and return the bytes
try
{
return Hex.decodeHex(s.toCharArray());
}
catch (Exception e)
{
throw (ParseException) new ParseException("Unable to parse the byte[] : " + e.getMessage());
}
}
/**
* Parse a date formatted as <*D2002-03-22 11:30:00 +0100>
*/
protected Date parseDate(String s) throws ParseException
{
return PropertyListConfiguration.parseDate(s);
}
}
PARSER_END(PropertyListParser)
SKIP : { " " | "\t" | "\n" | "\r" }
// Handle comments
MORE : { "/*": IN_COMMENT }
< IN_COMMENT > MORE : { < ~[] > }
< IN_COMMENT > SKIP : { "*/": DEFAULT }
MORE : { "//": IN_SINGLE_LINE_COMMENT }
< IN_SINGLE_LINE_COMMENT > SPECIAL_TOKEN : {
< SINGLE_LINE_COMMENT: "\n"|"\r"|"\r\n" > : DEFAULT }
< IN_SINGLE_LINE_COMMENT > MORE : { < ~[] > }
TOKEN : { <ARRAY_BEGIN : "(" > }
TOKEN : { <ARRAY_END : ")" > }
TOKEN : { <ARRAY_SEPARATOR : "," > }
TOKEN : { <DICT_BEGIN : "{" > }
TOKEN : { <DICT_END : "}" > }
TOKEN : { <DICT_SEPARATOR : ";" > }
TOKEN : { <EQUAL : "=" > }
TOKEN : { <DATA_START : "<" > }
TOKEN : { <DATA_END : ">" > }
TOKEN : { <DATE_START : "<*D" > }
TOKEN : { < QUOTE : "\"" > }
TOKEN : { < #LETTER : ~[" ", "\t", "\n", "\r", "(", ")", ",", "{", "}", ";", "=", "\""] > }
TOKEN : { < #WHITE : " " | "\t" | "\n" | "\r" > }
TOKEN : { < #HEXA : ["0"-"9", "a"-"f", "A"-"F"] > }
TOKEN : { < DATA : <DATA_START> (<HEXA> | <WHITE>)* <DATA_END> > }
TOKEN : { < DATE : <DATE_START> (["0"-"9"] | ":" | " " | "+" | "-" | "Z")* <DATA_END> > }
TOKEN : { < STRING : (<LETTER>)+ > }
TOKEN : { < QUOTED_STRING :
<QUOTE>
(<LETTER> | <WHITE> | <ESCAPED_QUOTE> | <EQUAL>
| <ARRAY_BEGIN> | <ARRAY_END> | <ARRAY_SEPARATOR>
| <DICT_BEGIN> | <DICT_END> | <DICT_SEPARATOR>)* <QUOTE> > }
TOKEN : { < ESCAPED_QUOTE : "\\\"" > }
PropertyListConfiguration parse() :
{
PropertyListConfiguration configuration = null;
}
{
configuration = Dictionary()
<EOF>
{ return configuration; }
}
PropertyListConfiguration Dictionary() :
{
ImmutableNode.Builder builder = new ImmutableNode.Builder();
ImmutableNode child = null;
}
{
<DICT_BEGIN>
(
child = Property()
{
if (child.getValue() instanceof HierarchicalConfiguration)
{
// prune & graft the nested configuration to the parent configuration
@SuppressWarnings("unchecked") // we created this configuration
HierarchicalConfiguration<ImmutableNode> conf =
(HierarchicalConfiguration<ImmutableNode>) child.getValue();
ImmutableNode root = conf.getNodeModel().getNodeHandler().getRootNode();
ImmutableNode.Builder childBuilder = new ImmutableNode.Builder();
childBuilder.name(child.getNodeName()).value(root.getValue())
.addChildren(root.getChildren());
builder.addChild(childBuilder.create());
}
else
{
builder.addChild(child);
}
}
)*
<DICT_END>
{
return new PropertyListConfiguration(builder.create());
}
}
ImmutableNode Property() :
{
String key = null;
Object value = null;
ImmutableNode.Builder node = new ImmutableNode.Builder();
}
{
key = String()
{ node.name(key); }
<EQUAL>
value = Element()
{ node.value(value); }
(<DICT_SEPARATOR>)?
{ return node.create(); }
}
Object Element() :
{
Object value = null;
}
{
LOOKAHEAD(2)
value = Array()
{ return value; }
|
value = Dictionary()
{ return value; }
|
value = String()
{ return value; }
|
value = Data()
{ return value; }
|
value = Date()
{ return value; }
}
List Array() :
{
List<Object> list = new ArrayList<Object>();
Object element = null;
}
{
<ARRAY_BEGIN>
(
element = Element()
{ list.add(element); }
(
<ARRAY_SEPARATOR>
element = Element()
{ list.add(element); }
)*
)?
<ARRAY_END>
{ return list; }
}
String String() :
{
Token token = null;
String value = null;
}
{
token = <QUOTED_STRING>
{ return unescapeQuotes(removeQuotes(token.image)); }
|
token = <STRING>
{ return token.image; }
}
byte[] Data() :
{
Token token;
}
{
token = <DATA>
{ return filterData(token.image); }
}
Date Date() :
{
Token token;
}
{
token = <DATE>
{ return parseDate(token.image); }
}