001/*
002 * Copyright (c) 2005 Einar Pehrson <einar@pehrson.nu>.
003 *
004 * This file is part of
005 * CleanSheets - a spreadsheet application for the Java platform.
006 *
007 * CleanSheets is free software; you can redistribute it and/or modify
008 * it under the terms of the GNU General Public License as published by
009 * the Free Software Foundation; either version 2 of the License, or
010 * (at your option) any later version.
011 *
012 * CleanSheets is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
015 * GNU General Public License for more details.
016 *
017 * You should have received a copy of the GNU General Public License
018 * along with CleanSheets; if not, write to the Free Software
019 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
020 */
021package csheets.core.formula.lang;
022
023import java.io.IOException;
024import java.io.InputStream;
025import java.lang.reflect.Method;
026import java.lang.reflect.Modifier;
027import java.util.ArrayList;
028import java.util.List;
029import java.util.Properties;
030
031import csheets.CleanSheets;
032import csheets.core.formula.BinaryOperator;
033import csheets.core.formula.Function;
034import csheets.core.formula.UnaryOperator;
035
036/**
037 * A factory for creating certain types of language elements.
038 * @author Einar Pehrson
039 */
040public class Language {
041
042        /** The singleton instance */
043        private static final Language instance = new Language();
044
045        /** The name of the file in which language properties are stored */
046        private static final String PROPERTIES_FILENAME = "res/language.props";
047
048        /** The unary operators that are supported by the language */
049        private List<UnaryOperator> unaryOperators = new ArrayList<UnaryOperator>();
050
051        /** The binary operators that are supported by the language */
052        private List<BinaryOperator> binaryOperators = new ArrayList<BinaryOperator>();
053
054        /** The functions that are supported by the language */
055        private List<Function> functions = new ArrayList<Function>();
056
057        /**
058         * Creates a new language.
059         */
060        private Language() {
061                // Loads properties
062                Properties language = new Properties();
063                InputStream stream = CleanSheets.class.getResourceAsStream(PROPERTIES_FILENAME);
064                if (stream != null) {
065                        try {
066                                language.load(stream);
067                        } catch (IOException e) {
068                                System.err.println("An I/O error occurred when loading language"
069                                        + " properties file (" + PROPERTIES_FILENAME + ").");
070                                return;
071                        } finally {
072                                try {
073                                        if (stream != null)
074                                                stream.close();
075                                } catch (IOException e) {}
076                        }
077
078                        // Loads elements
079                        for (Object className : language.keySet()) {
080                                // Loads class and instantiates element
081                                Class elementClass;
082                                Object element;
083                                try {
084                                        elementClass = Class.forName(getClass().getPackage()
085                                                .getName() + "." + (String)className);
086                                        element = elementClass.newInstance();
087                                } catch (Exception e) {
088                                        // Skip this element, regardless of what went wrong
089                                        continue;
090                                }
091
092                                // Stores element
093                                if (Function.class.isAssignableFrom(elementClass))
094                                        functions.add(Function.class.cast(element));
095                                if (BinaryOperator.class.isAssignableFrom(elementClass))
096                                        binaryOperators.add(BinaryOperator.class.cast(element));
097                                if (UnaryOperator.class.isAssignableFrom(elementClass))
098                                        unaryOperators.add(UnaryOperator.class.cast(element));
099                        }
100                } else
101                        System.err.println("Could not find language properties file ("
102                                + PROPERTIES_FILENAME + ").");
103
104                // Loads static methods from java.lang.Math that use double precision
105                for (Method method : Math.class.getMethods())
106                        if (Modifier.isStatic(method.getModifiers()) &&
107                                                method.getReturnType() == Double.TYPE)
108                                functions.add(new NumericFunction(method));
109        }
110
111        /**
112         * Returns the singleton instance.
113         * @return the singleton instance
114         */
115        public static Language getInstance() {
116                return instance;
117        }
118
119        /**
120         * Returns the unary operator with the given identifier.
121         * @return the unary operator with the given identifier
122         */
123        public UnaryOperator getUnaryOperator(String identifier) throws UnknownElementException {
124                for (UnaryOperator operator : unaryOperators)
125                        if (identifier.equalsIgnoreCase(operator.getIdentifier()))
126                                return operator; // .clone()
127                throw new UnknownElementException(identifier);
128        }
129
130        /**
131         * Returns the binary operator with the given identifier.
132         * @return the binary operator with the given identifier
133         */
134        public BinaryOperator getBinaryOperator(String identifier) throws UnknownElementException {
135                for (BinaryOperator operator : binaryOperators)
136                        if (identifier.equalsIgnoreCase(operator.getIdentifier()))
137                                return operator; // .clone()
138                throw new UnknownElementException(identifier);
139        }
140
141        /**
142         * Returns the function with the given identifier.
143         * @return the function with the given identifier
144         */
145        public Function getFunction(String identifier) throws UnknownElementException {
146                for (Function function : functions)
147                        if (identifier.equalsIgnoreCase(function.getIdentifier()))
148                                return function; // .clone()
149                throw new UnknownElementException(identifier);
150        }
151
152        /**
153         * Returns whether there is a function with the given identifier.
154         * @return whether there is a function with the given identifier
155         */
156        public boolean hasFunction(String identifier) {
157                try {
158                        return getFunction(identifier) != null;
159                } catch (UnknownElementException e) {
160                        return false;
161                }
162        }
163
164        /**
165         * Returns the functions that are supported by the syntax.
166         * @return the functions that are supported by the syntax
167         */
168        public Function[] getFunctions() {
169                return functions.toArray(new Function[functions.size()]);
170        }
171}