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     */
021    package csheets.core.formula.lang;
022    
023    import java.io.IOException;
024    import java.io.InputStream;
025    import java.lang.reflect.Method;
026    import java.lang.reflect.Modifier;
027    import java.util.ArrayList;
028    import java.util.List;
029    import java.util.Properties;
030    
031    import csheets.CleanSheets;
032    import csheets.core.formula.BinaryOperator;
033    import csheets.core.formula.Function;
034    import csheets.core.formula.UnaryOperator;
035    
036    /**
037     * A factory for creating certain types of language elements.
038     * @author Einar Pehrson
039     */
040    public 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    }