    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}