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