001package org.cpsolver.ifs.util;
002
003import java.io.BufferedReader;
004import java.io.FileReader;
005import java.io.IOException;
006import java.io.InputStreamReader;
007import java.io.PrintWriter;
008import java.io.Reader;
009import java.util.ArrayList;
010import java.util.Iterator;
011import java.util.List;
012
013/**
014 * A class for reading prolog files.
015 * 
016 * @version IFS 1.3 (Iterative Forward Search)<br>
017 *          Copyright (C) 2006 - 2014 Tomáš Müller<br>
018 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
019 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
020 * <br>
021 *          This library is free software; you can redistribute it and/or modify
022 *          it under the terms of the GNU Lesser General Public License as
023 *          published by the Free Software Foundation; either version 3 of the
024 *          License, or (at your option) any later version. <br>
025 * <br>
026 *          This library is distributed in the hope that it will be useful, but
027 *          WITHOUT ANY WARRANTY; without even the implied warranty of
028 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
029 *          Lesser General Public License for more details. <br>
030 * <br>
031 *          You should have received a copy of the GNU Lesser General Public
032 *          License along with this library; if not see
033 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
034 */
035public class PrologFile implements Iterator<PrologFile.Term> {
036    private BufferedReader iBufferedReader = null;
037    private Term iNextTerm = null;
038
039    public PrologFile(String file) throws java.io.IOException {
040        iBufferedReader = new BufferedReader(new FileReader(file));
041        iNextTerm = (iBufferedReader.ready() ? readTerm(new SpecialReader(iBufferedReader)) : null);
042        if (iNextTerm == null)
043            iBufferedReader.close();
044        else
045            iBufferedReader.readLine();
046    }
047
048    /** Reads a prolog file. It returns a set of terms 
049     * @param is input stream
050     * @param term term to read
051     * @return list of terms
052     * @throws java.io.IOException an exception
053     **/
054    public static List<Term> readTermsFromStream(java.io.InputStream is, String term) throws java.io.IOException {
055        BufferedReader br = new BufferedReader(new InputStreamReader(is));
056        List<Term> ret = new ArrayList<Term>();
057        // int x=0;
058        while (br.ready()) {
059            Term t = readTerm(new SpecialReader(br));
060            // System.out.println(t);
061            // x++;
062            // if (x>10) break;
063            if (t != null && t.getText() != null && t.getText().startsWith(term)) {
064                ret.add(t);
065            }
066            br.readLine();
067        }
068        br.close();
069        return ret;
070    }
071
072    /** Writes a set of terms. 
073     * @param pw print writer
074     * @param terms list of terms to write
075     * @throws java.io.IOException an exception
076     **/
077    public static void writeTerms(PrintWriter pw, List<Term> terms) throws java.io.IOException {
078        for (Term t : terms) {
079            writeTerm(pw, t);
080        }
081    }
082
083    /** reads a term */
084    private static Term readTerm(SpecialReader is) throws IOException {
085        StringBuffer text = new StringBuffer();
086        List<Term> content = null;
087        int i;
088        if ((i = is.read()) >= 0) {
089            while ((char) i == '%' || (char) i == ':') {
090                do {
091                    i = is.read();
092                } while (i >= 0 && !(i == 0x0d || i == 0x0a));
093                i = is.read();
094                if (i >= 0 && (i == 0x0d || i == 0x0a))
095                    i = is.read();
096            }
097            if (i >= 0)
098                is.flush((char) i);
099        }
100        char prev = (char) i;
101        if (i >= 0)
102            while ((i = is.read()) >= 0) {
103                char ch = (char) i;
104                if (ch == '\n' || ch == '\r')
105                    if (prev == '.')
106                        break;
107                    else
108                        continue;
109                if (ch == '(' || ch == '[') {
110                    content = new ArrayList<Term>();
111                    content.add(readTerm(is));
112                } else if (content == null && (ch == ',' || ch == ')' || ch == ']')) {
113                    is.flush(ch);
114                    break;
115                } else if (ch == ',')
116                    content.add(readTerm(is));
117                else if (ch == ')' || ch == ']')
118                    break;
119                else
120                    text.append(ch);
121                prev = ch;
122            }
123        else
124            return null;
125        Term ret = new Term(text.toString().trim(), content);
126        return ret;
127    }
128
129    /** writes a term */
130    private static void writeTerm(PrintWriter pw, Term t) {
131        pw.println(t.toString() + ".");
132    }
133
134    @Override
135    public boolean hasNext() {
136        return iNextTerm != null;
137    }
138
139    @Override
140    public Term next() {
141        Term ret = iNextTerm;
142        try {
143            iNextTerm = (iBufferedReader.ready() ? readTerm(new SpecialReader(iBufferedReader)) : null);
144        } catch (java.io.IOException x) {
145            iNextTerm = null;
146        }
147        try {
148            if (iNextTerm == null)
149                iBufferedReader.close();
150            else
151                iBufferedReader.readLine();
152        } catch (java.io.IOException x) {
153        }
154        return ret;
155    }
156
157    @Override
158    public void remove() {
159    }
160
161    /** Flushable reader -- extension of java.io.Reader */
162    private static class SpecialReader {
163        /** reader */
164        private Reader iReader = null;
165        /** flushed characters */
166        private StringBuffer iFlushedChars = new StringBuffer();
167
168        /** constructor 
169         * @param r a reader to wrap
170         **/
171        public SpecialReader(Reader r) {
172            iReader = r;
173        }
174
175        /** reads a byte 
176         * @return a byte that was read
177         * @throws java.io.IOException an exception thrown by the parent reader */
178        public int read() throws java.io.IOException {
179            if (iFlushedChars.length() == 0)
180                return iReader.read();
181            char ret = iFlushedChars.charAt(0);
182            iFlushedChars.deleteCharAt(0);
183            return ret;
184        }
185
186        /** flush (return to stream) a character 
187         * @param ch a character to be returned back 
188         **/
189        public void flush(char ch) {
190            iFlushedChars.insert(0, ch);
191        }
192    }
193
194    /** Term -- it can contain a text and a content (set of terms) */
195    public static class Term {
196        /** text */
197        private String iText = null;
198        /** content */
199        private List<Term> iContent = null;
200
201        @Override
202        public boolean equals(Object o) {
203            if (o == null || !(o instanceof Term))
204                return false;
205            Term t = (Term) o;
206            if (iText == null && t.iText != null)
207                return false;
208            if (iText != null && t.iText == null)
209                return false;
210            if (iText != null && !iText.equals(t.iText))
211                return false;
212            if (iContent == null && t.iContent != null)
213                return false;
214            if (iContent != null && t.iContent == null)
215                return false;
216            if (iContent != null && !iContent.equals(t.iContent))
217                return false;
218            return true;
219        }
220
221        /** constructor 
222         * @param text name of the term
223         **/
224        public Term(String text) {
225            iText = text;
226            iContent = null;
227        }
228
229        /** constructor 
230         * @param content inner terms 
231         **/
232        public Term(List<Term> content) {
233            iText = null;
234            iContent = content;
235        }
236
237        /** constructor 
238         * @param text name of the term
239         * @param content inner terms
240         **/
241        public Term(String text, List<Term> content) {
242            iText = text;
243            iContent = content;
244        }
245
246        /** constructor 
247         * @param text name of the term
248         * @param content inner terms
249         **/
250        public Term(String text, Term[] content) {
251            iText = text;
252            if (content == null) {
253                iContent = null;
254            } else {
255                iContent = new ArrayList<Term>();
256                for (int i = 0; i < content.length; i++)
257                    iContent.add(content[i]);
258            }
259        }
260
261        /** constructor
262         * @param content inner terms
263         **/
264        public Term(Term[] content) {
265            this(null, content);
266        }
267
268        /** return text
269         * @return term name
270         */
271        public String getText() {
272            return iText;
273        }
274
275        /** return content
276         * @return term content (inner terms)
277         */
278        public List<Term> getContent() {
279            return iContent;
280        }
281
282        /** content size 
283         * @return number of inner terms
284         **/
285        public int size() {
286            return (iContent == null ? -1 : iContent.size());
287        }
288
289        /** return text as int 
290         * @return term name as int
291         **/
292        public int toInt() {
293            return Integer.parseInt(iText);
294        }
295
296        /** return text as long
297         * @return term name as long
298         */
299        public long toLong() {
300            return Long.parseLong(iText);
301        }
302
303        /** return text as double
304         * @return term name as double
305         */
306        public double toDouble() {
307            return Double.parseDouble(iText);
308        }
309
310        /** return text as boolean
311         * @return term name as boolean
312         */
313        public boolean toBoolean() {
314            return (toInt() != 0);
315        }
316
317        /** return content as boolean array
318         * @return inner terms as booleans
319         */
320        public boolean[] toBooleanArray() {
321            if (iContent.size() == 1 && iContent.get(0).toString().length() == 0)
322                return new boolean[] {};
323            boolean[] ret = new boolean[iContent.size()];
324            for (int i = 0; i < ret.length; i++) {
325                ret[i] = elementAt(i).toBoolean();
326            }
327            return ret;
328        }
329
330        /** return content as string array
331         * @return inner terms as strings
332         */
333        public String[] toStringArray() {
334            if (iContent.size() == 1 && iContent.get(0).toString().length() == 0)
335                return new String[] {};
336            String[] ret = new String[iContent.size()];
337            for (int i = 0; i < ret.length; i++) {
338                Term t = elementAt(i);
339                ret[i] = (t.getText().length() > 0 ? t.toString() : t.elementAt(0).toString());
340            }
341            return ret;
342        }
343
344        /** return content as int array
345         * @return inner terms as ints
346         */
347        public int[] toIntArray() {
348            // System.err.println("ToIntArray: "+this);
349            if (iContent.size() == 1 && iContent.get(0).toString().length() == 0)
350                return new int[] {};
351            int[] ret = new int[iContent.size()];
352            for (int i = 0; i < ret.length; i++) {
353                Term t = elementAt(i);
354                ret[i] = (t.getText().length() > 0 ? Integer.parseInt(t.getText()) : t.elementAt(0).toInt());
355                // System.err.println("  "+i+" .. "+ret[i]);
356            }
357            return ret;
358        }
359
360        /** idx-th element of content
361         * @param idx index of the inner term
362         * @return inner term
363         */
364        public Term elementAt(int idx) {
365            try {
366                return iContent.get(idx);
367            } catch (Exception e) {
368                return null;
369            }
370        }
371
372        /** element of content named name 
373         * @param name name of the inner term
374         * @return inner term, null if not present
375         **/
376        public Term element(String name) {
377            try {
378                for (Term t : iContent) {
379                    if (t.getText() != null && t.getText().equals(name))
380                        return t;
381                }
382                return null;
383            } catch (Exception e) {
384                return null;
385            }
386        }
387
388        /** index of element of content named name 
389         * @param name name of the inner term
390         * @return index of the inner term, -1 if not present
391         */
392        public int indexOf(String name) {
393            try {
394                int idx = 0;
395                for (Term t : iContent) {
396                    if (t.getText() != null && t.getText().equals(name))
397                        return idx;
398                    idx++;
399                }
400                return -1;
401            } catch (Exception e) {
402                return -1;
403            }
404        }
405
406        /** string representation of term */
407        @Override
408        public String toString() {
409            boolean isArray = (iText == null || iText.length() == 0);
410            StringBuffer sb = new StringBuffer(isArray ? "" : iText);
411            if (iContent != null) {
412                sb.append(isArray ? "[" : "(");
413                for (Iterator<Term> e = iContent.iterator(); e.hasNext();) {
414                    sb.append(e.next().toString());
415                    sb.append(e.hasNext() ? "," : "");
416                }
417                sb.append(isArray ? "]" : ")");
418            }
419            return sb.toString();
420        }
421
422        @Override
423        public int hashCode() {
424            return toString().hashCode();
425        }
426
427        @Override
428        public Object clone() {
429            return new Term(iText == null ? null : new String(iText), iContent == null ? iContent
430                    : new ArrayList<Term>(iContent));
431        }
432    }
433}