001    //This file is part of the Attempto Java Packages.
002    //Copyright 2008-2009, Attempto Group, University of Zurich (see http://attempto.ifi.uzh.ch).
003    
004    //The Attempto Java Packages is free software: you can redistribute it and/or modify it under the
005    //terms of the GNU Lesser General Public License as published by the Free Software Foundation,
006    //either version 3 of the License, or (at your option) any later version.
007    
008    //The Attempto Java Packages is distributed in the hope that it will be useful, but WITHOUT ANY
009    //WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
010    //PURPOSE. See the GNU Lesser General Public License for more details.
011    
012    //You should have received a copy of the GNU Lesser General Public License along with the Attempto
013    //Java Packages. If not, see http://www.gnu.org/licenses/.
014    
015    package ch.uzh.ifi.attempto.ape;
016    
017    import jpl.Atom;
018    import jpl.Compound;
019    import jpl.JPL;
020    import jpl.Query;
021    import jpl.Term;
022    import jpl.Util;
023    import jpl.Variable;
024    
025    /**
026     * This class provides an interface to the SWI Prolog executable of the Attempto Parsing Engine
027     * (APE). Note that you need the file "ape.exe" (which can be compiled from the Attempto APE
028     * distribution) and that SWI Prolog needs to be installed.
029     * Furthermore, you have to make sure that the JPL libraries of SWI Prolog are in the java library
030     * path. This can be achieved by a Java VM argument which looks like this (for Mac OS X):
031     * 
032     * <blockquote><code>
033     *   -Djava.library.path=/opt/local/lib/swipl-5.6.45/lib/i386-darwin8.10.1
034     * </code></blockquote>
035     * 
036     * Under Linux, the following environment variable has to be set additionally:
037     * 
038     * <blockquote><code>
039     *   LD_PRELOAD=/usr/lib/pl-5.6.45/lib/i386-linux/libjpl.so
040     * </code></blockquote>
041     * 
042     * The exact paths are most probably different on your machine. Just look for the directory that
043     * contains the file or symbolic link <em>libjpl.jnilib</em> (under Mac OS X), <em>jpl.dll</em>
044     * (under Windows), or <em>libjpl.so</em> (under Unix). If you get the error message
045     * 
046     * <blockquote><code>
047     *   java.lang.UnsatisfiedLinkError: no jpl in java.library.path
048     * </code></blockquote>
049     * 
050     * then this is a strong indication that the SWI Prolog JPL libraries are not found.
051     * 
052     * 
053     * @author Tobias Kuhn
054     */
055    public class APELocal extends ACEParser {
056    
057            private static APELocal apeLocal;
058    
059            /**
060             * Creates a new parser object. You can create at most one such object per JVM. If you
061             * try to create more than one such object or if you called the init method before
062             * then a runtime exception is thrown.
063             * 
064             * @param apeExeFile The path (with filename) of the file "ape.exe".
065             */
066            public APELocal(String apeExeFile) {
067                    if (apeLocal != null) {
068                            throw new RuntimeException("Only one APELocal object can be created.");
069                    }
070                    
071                    // A problem was reported that might be solved by using the --nosignals option.
072                    // Otherwise, it seems to have no effect in our case.
073                    // The first argument "swipl" has no effect and could be any other string.
074                    JPL.init(new String[] {"swipl", "-x", apeExeFile, "-g", "true", "--nosignals"});
075                    
076                    apeLocal = this;
077            }
078    
079            /**
080             * Creates a new parser object. You can create at most one such object per JVM. If you
081             * try to create more than one such object or if you called the init method before
082             * then a runtime exception is thrown.
083             * <br>
084             * This method is deprecated because the {@code prologCommand} argument is actually not needed.
085             * Use {@link #APELocal(String)} instead.
086             * 
087             * @param prologCommand The command to run the SWI Prolog interpreter.
088             *     On Windows this is usually "plcon", on Linux "pl", and on Mac "swipl".
089             * @param apeExeFile The path (with filename) of the file "ape.exe".
090             */
091            @Deprecated
092            public APELocal(String prologCommand, String apeExeFile) {
093                    this(apeExeFile);
094            }
095    
096            /**
097             * Returns the singleton APELocal instance. Null is returned if the APELocal instance has not yet
098             * been initialized by the constructor or the init method.
099             * 
100             * @return The singleton APELocal instance.
101             */
102            public static APELocal getInstance() {
103                    return apeLocal;
104            }
105    
106            /**
107             * Initializes the APELocal singleton instance. This method can be called at most once.
108             * If you call it twice or if you called the constructor before then a runtime exception
109             * is thrown.
110             * 
111             * @param apeExeFile The path (with filename) of the file "ape.exe".
112             */
113            public static void init(String apeExeFile) {
114                    new APELocal(apeExeFile);
115            }
116    
117            /**
118             * Initializes the APELocal singleton instance. This method can be called at most once.
119             * If you call it twice or if you called the constructor before then a runtime exception
120             * is thrown.
121             * <br>
122             * This method is deprecated because the {@code prologCommand} argument is actually not needed.
123             * Use {@link #init(String)} instead.
124             * 
125             * @param prologCommand The command to run the SWI Prolog interpreter.
126             *     On Windows this is usually "plcon", on Linux "pl", and on Mac "swipl".
127             * @param apeExeFile The path (with filename) of the file "ape.exe".
128             */
129            @Deprecated
130            public static void init(String prologCommand, String apeExeFile) {
131                    new APELocal(prologCommand, apeExeFile);
132            }
133    
134            /**
135             * Checks whether the singleton instance has already been initialized.
136             * 
137             * @return true if the singleton instance has been initialized.
138             */
139            public static boolean isInitialized() {
140                    return apeLocal != null;
141            }
142    
143            public synchronized String getSoloOutput(String text, Lexicon lexicon, OutputType outputType) throws ACEParserException {
144                    clearMessages();
145                    String ulextext = "";
146                    if (lexicon != null) {
147                            ulextext = ",ulextext=" + PrologUtils.escape(lexicon.toString());
148                    }
149                    Term input = Util.textToTerm("[text=" + PrologUtils.escape(text) + ulextext + ",solo=" + outputType.toString().toLowerCase() + getOptions() + "]");
150                    Query q = new Query("get_ape_results", new Term[] {input, new Variable("Result")});
151                    Atom result = (Atom) q.oneSolution().get("Result");
152                    String s = result.name();
153    
154                    return checkForErrors(s);
155            }
156    
157            public synchronized ACEParserResult getMultiOutput(String text, Lexicon lexicon, OutputType... outputTypes) {
158                    clearMessages();
159                    String outputs = "";
160                    for (OutputType t : outputTypes) {
161                            outputs += ",c" + t.toString().toLowerCase() + "=on";
162                    }
163                    String ulextext = "";
164                    if (lexicon != null) {
165                            ulextext = ",ulextext=" + PrologUtils.escape(lexicon.toString());
166                    }
167                    Term input = Util.textToTerm("[text=" + PrologUtils.escape(text) + ulextext + outputs + getOptions() + "]");
168                    Query q = new Query("get_ape_results", new Term[] {input, new Variable("Result")});
169                    Atom result = (Atom) q.oneSolution().get("Result");
170                    return new ACEParserResult(result.name());
171            }
172    
173            /**
174             * Loads the lexicon by adding all lexicon entries of the lexicon. Note that these lexicon entries
175             * are automatically discarded the first time you call getSoloOutput or getMultiOutput with a lexicon
176             * parameter that is not null.
177             * 
178             * @param lexicon The lexicon to be loaded.
179             * @see #addLexiconEntry
180             * @see #discardLexicon
181             */
182            public synchronized void addLexicon(Lexicon lexicon) {
183                    clearMessages();
184                    Query q = new Query(new Compound("add_lexicon_entries", new Term[] { Util.textToTerm(lexicon.toList()) }));
185                    q.oneSolution();
186            }
187    
188            /**
189             * Adds a new lexicon entry. Note that this lexicon entry is automatically discarded the first time
190             * you call getSoloOutput or getMultiOutput with a lexicon parameter that is not null.
191             * 
192             * @param lexiconEntry The lexicon entry to be added.
193             * @see #addLexicon
194             * @see #discardLexicon
195             */
196            public synchronized void addLexiconEntry(LexiconEntry lexiconEntry) {
197                    clearMessages();
198                    Query q = new Query(new Compound("add_lexicon_entry", new Term[] { Util.textToTerm(lexiconEntry.toString()) }) );
199                    q.oneSolution();
200            }
201    
202            /**
203             * Discards the dynamically added lexicon entries. Note that the lexicon entries that are complied into
204             * the SWI Prolog executable are not affected by this operation.
205             *
206             * @see #addLexiconEntry
207             * @see #addLexicon
208             */
209            public synchronized void discardLexicon() {
210                    clearMessages();
211                    Query q = new Query("discard_ulex");
212                    q.oneSolution();
213            }
214    
215            private void clearMessages() {
216                    Query q = new Query("clear_messages");
217                    q.oneSolution();
218            }
219    }