All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.eva.properties.PropertiesParser Maven / Gradle / Ivy

Go to download

Advanced properties with object factories, references and inheritance.

There is a newer version: 0.3
Show newest version
/*
 * $Id: PropertiesParser.java 43 2007-02-27 21:30:21Z max $
 * 
 * Copyright (c) 2006-2007 Maximilian Antoni. All rights reserved.
 * 
 * This software is licensed as described in the file LICENSE.txt, which you
 * should have received as part of this distribution. The terms are also
 * available at http://www.maxantoni.de/projects/eva-properties/license.txt.
 */
package com.eva.properties;

import java.io.IOException;
import java.io.Reader;
import java.io.StreamTokenizer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * parses property files.
 * 
 * @author Max Antoni
 * @version $Revision: 43 $
 */
class PropertiesParser {
    private StreamTokenizer st;
    private Properties current;
    private boolean proxyParent;
    
    private PropertiesParser(Reader inReader, boolean inProxyParent) {
        super();
        if(inReader == null) {
            throw new NullPointerException();
        }
        st = new StreamTokenizer(inReader);
        st.resetSyntax();
        st.wordChars('a', 'z');
        st.wordChars('A', 'Z');
        st.parseNumbers();
        st.wordChars(128 + 32, 255);
        st.wordChars('_', '_');
        st.whitespaceChars(0, ' ');
        st.quoteChar('"');
        st.quoteChar('\'');
        st.ordinaryChar('.');
        st.eolIsSignificant(true);
        st.slashSlashComments(true);
        st.slashStarComments(true);
        proxyParent = inProxyParent;
    }
    
    static Properties read(Properties inParent, DataSource inDataSource)
            throws IOException {
        try {
            return new PropertiesParser(inDataSource.getReader(), true)
                    .mapOrList(inParent);
        }
        catch(IllegalStateException e) {
            throw new PropertiesException(e.getMessage());
        }
    }
    
    static Properties read(DataSource inDataSource, boolean inProxyParent)
            throws IOException {
        Properties properties;
        try {
            properties = new PropertiesParser(inDataSource.getReader(),
                    inProxyParent).mapOrList(null);
        }
        catch(IllegalStateException e) {
            throw new PropertiesException(e.getMessage());
        }
        if(properties instanceof MapProperties) {
            initMap((MapProperties) properties, inDataSource);
        }
        else {
            initList((ListProperties) properties, inDataSource);
        }
        return properties;
    }
    
    private static void initList(ListProperties inoutList,
            DataSource inDataSource) {
        for(Iterator i = inoutList.iterator(); i.hasNext();) {
            Object o = i.next();
            if(o instanceof MapProperties) {
                initMap((MapProperties) o, inDataSource);
            }
        }
    }

    private static void initMap(MapProperties inoutMap,
            DataSource inDataSource) {
        if(!inoutMap.containsKeyInternal(PropertiesFactory.DATASOURCE_BASE)) {
            inoutMap.putInternal(PropertiesFactory.DATASOURCE_BASE, inDataSource
                    .getDelegateBase());
        }
        ClassLoader classLoader = inDataSource.getClassLoader();
        if(classLoader != null) {
            inoutMap.put("classloader", classLoader);
        }
    }

    
    static Object readObject(Reader inReader, boolean inProxyParent)
            throws IOException {
        try {
            return new PropertiesParser(inReader, inProxyParent).primary(true);
        }
        catch(IllegalStateException e) {
            throw new PropertiesException(e.getMessage());
        }
    }
    
    static void readMap(MapProperties inoutProperties, Reader inReader,
            boolean inProxyParent) throws IOException {
        PropertiesParser parser = new PropertiesParser(inReader, inProxyParent);
        parser.current = inoutProperties;
        try {
            parser.map(inoutProperties, StreamTokenizer.TT_EOF);
        }
        catch(IllegalStateException e) {
            throw new PropertiesException(e.getMessage());
        }
    }
    
    static void readList(ListProperties inoutProperties, Reader inReader,
            boolean inProxyParent) throws IOException {
        PropertiesParser parser = new PropertiesParser(inReader, inProxyParent);
        parser.current = inoutProperties;
        parser.list(inoutProperties, StreamTokenizer.TT_EOF);
    }
    
    private Properties mapOrList(Properties inParent) throws IOException {
        Object primary;
        try {
            primary = primary(true);
        }
        catch(IllegalStateException e) {
            // Handle non-primary types:
            switch(st.ttype) {
            case '*':
                primary = "*";
                st.nextToken();
                break;
            case ':':
                if(!"*".equals(e.getMessage())) {
                    throw e;
                }
                primary = "*";
                break;
            default:
                throw e;
            }
        }
        if(st.ttype == ':') {
            if(!(primary instanceof String)) {
                throw new PropertiesException("String expected, "
                        + primary.getClass().getName());
            }
            MapProperties map = new MapProperties(inParent);
            current = map;
            map.putInternal((String) primary, primary(true));
            map(map, StreamTokenizer.TT_EOF);
            return map;
        }
        ListProperties list = new ListProperties(inParent);
        current = list;
        if(primary instanceof Properties) {
            ((Properties) primary).setParent(list);
        }
        list.add(primary);
        if(st.ttype == StreamTokenizer.TT_EOF) {
            return current;
        }
        list(list, StreamTokenizer.TT_EOF);
        return list;
    }

    private String token(int inToken) {
        switch (inToken) {
        case StreamTokenizer.TT_WORD:
            return st.sval;
        case '\'':
            return '\'' + st.sval + '\'';
        case '"':
            return '"' + st.sval + '"';
        case StreamTokenizer.TT_NUMBER:
            return String.valueOf(st.nval);
        case StreamTokenizer.TT_EOF:
            return "EOF";
        case StreamTokenizer.TT_EOL:
            return "EOL";
        default:
            return "" + (char) inToken;
        }
    }
    
    private String error() {
        return token(st.ttype) + " line " + st.lineno();
    }
    
    private Number number(double inNumber) throws IOException {
        if(st.nextToken() == '.') {
            if(st.nextToken() != StreamTokenizer.TT_NUMBER) {
                throw new IllegalStateException("Number expected, " + error());
            }
            st.nextToken();
            return Double.valueOf(inNumber + "." + st.nval);
        }
        if(((long) inNumber) == inNumber) {
            return new Long((long) inNumber);
        }
        return new Double(inNumber);
    }
    
    private Object primary(boolean inGet) throws IOException {
        int token = inGet ? st.nextToken() : st.ttype;
        while(token == StreamTokenizer.TT_EOL) {
            token = st.nextToken();
        }
        switch(token) {
        case StreamTokenizer.TT_WORD:
            String s = st.sval;
            st.nextToken();
            if("null".equals(s) || "nil".equals(s)) {
                return Null.INSTANCE;
            }
            if("true".equals(s) || "yes".equals(s) || "on".equals(s)) {
                return Boolean.TRUE;
            }
            if("false".equals(s) || "no".equals(s) || "off".equals(s)) {
                return Boolean.FALSE;
            }
            return s;
        case StreamTokenizer.TT_NUMBER:
            return number(st.nval);
        case '\'':
            s = st.sval;
            st.nextToken();
            if(s.length() == 1) {
                return new Character(s.charAt(0));
            }
            return s.toCharArray();
        case '"':
            s = st.sval;
            st.nextToken();
            return s;
        case '[':
            ListProperties list = new ListProperties(current);
            current = list;
            list(list, ']');
            current = list.getParent();
            return list;
        case '{':
            MapProperties map = new MapProperties(current);
            current = map;
            map(map, '}');
            current = map.getParent();
            return map;
        case '$':
            return reference();
        case '.':
            return doubleNumber();
        case '(':
            return switcher();
        case '&':
            return proxy();
        case '*':
            return factory();
        default:
            throw new PropertiesException("Unexpected token, " + error());
        }
    }
    
    private Factory factory() throws IOException {
        List arguments = null;
        StringBuffer className = new StringBuffer();
        while(true) {
            switch (st.nextToken()) {
            case StreamTokenizer.TT_WORD:
                className.append(st.sval);
                break;
            case '.':
                className.append('.');
                break;
            case '$':
                className.append('$');
                break;
            case '{':
                className.append('{');
                break;
            case '}':
                className.append('}');
                break;
            case '(':
                arguments = list(')');
                return new Factory(className.toString(), arguments.toArray());
            case ':':
                if(className.length() == 0) {
                    /*
                     * Happens when the first token in a map properties file is
                     * a "*" that is meant to be a joker. The exception will be
                     * catched by mapOrList(...).
                     */
                    throw new IllegalStateException("*");
                }
            default:
                return new Factory(className.toString(), null);
            }
        }
    }
    
    private Proxy proxy() throws IOException, PropertiesException {
        Object object = primary(true);
        if(object instanceof String) {
            return new Proxy(proxyParent ? current : null, (String) object);
        }
        if(object instanceof Replaceable) {
            return new Proxy(proxyParent ? current : null, (Replaceable) object);
        }
        throw new PropertiesException("String or Replaceable expected, "
                + object.getClass().getName());
    }

    private Switch switcher() throws IOException {
        st.nextToken();
        Switch s = new Switch();
        while(st.ttype != ')') {
            s.add(primary(false));
            if(st.ttype != ',' && st.ttype != StreamTokenizer.TT_EOL) {
                if(st.ttype == ')') {
                    break;
                }
                throw new PropertiesException(") or , or EOL expected, "
                        + error());
            }
            overreadEmptyLines();
        }
        st.nextToken();
        return s;
    }
    
    private void overreadEmptyLines() throws IOException {
        do {
            st.nextToken();
        }
        while(st.ttype == StreamTokenizer.TT_EOL);
    }
    
    private Double doubleNumber() throws IOException {
        if(st.nextToken() != StreamTokenizer.TT_NUMBER) {
            throw new PropertiesException("Number expected, " + error());
        }
        return new Double(st.nval);
    }

    private Reference reference() throws IOException {
        if(st.nextToken() != '{') {
            throw new PropertiesException("{ expected, " + error());
        }
        StringBuffer reference = new StringBuffer();
        while(st.nextToken() != '}') {
            switch(st.ttype) {
            case StreamTokenizer.TT_WORD:
                reference.append(st.sval);
                break;
            case StreamTokenizer.TT_NUMBER:
                if(((long) st.nval) == st.nval) {
                    reference.append((long) st.nval);
                }
                else {
                    reference.append(st.nval);
                }
                break;
            case '.':
                reference.append('.');
                break;
            case '*':
                reference.append('*');
                break;
            default:
                throw new PropertiesException("} expected, " + error());
            }
        }
        st.nextToken();
        return new Reference(reference.toString());
    }

    private void map(MapProperties inoutMap, int inToToken) throws IOException {
        overreadEmptyLines();
        while(st.ttype != inToToken) {
            String key = key();
            if(st.nextToken() != ':') {
                throw new PropertiesException(": expected, " + error());
            }
            inoutMap.putInternal(key, primary(true));
            if(st.ttype != ',' && st.ttype != StreamTokenizer.TT_EOL) {
                if(st.ttype == inToToken) {
                    break;
                }
                throw new PropertiesException(token(inToToken) + " expected, "
                        + error());
            }
            overreadEmptyLines();
        }
        st.nextToken();
    }

    private List list(int inToToken) throws IOException {
        List list = new ArrayList();
        list(list, inToToken);
        return list;
    }
    
    private void list(List inoutList, int inToToken) throws IOException {
        overreadEmptyLines();
        while(st.ttype != inToToken) {
            inoutList.add(primary(false));
            if(st.ttype != ',' && st.ttype != StreamTokenizer.TT_EOL) {
                if(st.ttype == inToToken) {
                    break;
                }
                throw new PropertiesException(token(inToToken)
                        + " expected, " + error());
            }
            overreadEmptyLines();
        }
        st.nextToken();
    }
    
    private String key() throws IOException {
        while(st.ttype == StreamTokenizer.TT_EOL) {
            st.nextToken();
        }
        switch (st.ttype) {
        case StreamTokenizer.TT_NUMBER:
            if(((int) st.nval) == st.nval) {
                return String.valueOf((long) st.nval);
            }
            return String.valueOf(st.nval);
        case StreamTokenizer.TT_WORD:
        case '"':
        case '\'':
            return st.sval;
        case '*':
            return "*";
        default:
            throw new PropertiesException("Unexpected token, " + error());
        }
    }
 
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy