Subversion Repositories mkgmap

Rev

Rev 2470 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * Copyright (C) 2010.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 or
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 */


package uk.me.parabola.mkgmap.osmstyle.eval;

import uk.me.parabola.mkgmap.reader.osm.Element;

/**
 * This is used to implement OR expressions as if each term was a separate
 * indexable expression.
 *
 * <p>So if you have:
 * <pre>
 * A=1 | B=1 | C=1 {set name='${name} ${A}' | '${B}'; }
 * </pre>
 * This is represented by:
 * <pre>
 * A=1 {set name='${name} ${A}' | '${B}'; }
 * B=1 {set name='${name} ${A}' | '${B}'; }
 * C=1 {set name='${name} ${A}' | '${B}'; }
 * </pre>
 * So that each term can be index separately.  However we need to be able to
 * prevent the second and/or third terms running if the first (or second) matches.
 * That is what this class does.  It acts in most respects like the operation
 * it is wrapping, but when a successful evaluation is performed on an
 * element, the test is suppressed for the subsequent terms in the chain.
 *
 * @author Steve Ratcliffe
 */

public class LinkedOp implements Op {
        protected final Op wrapped;
        private final boolean first;
        private LinkedOp link;
        private Element current;

        protected LinkedOp(Op wrapped, boolean first) {
                this.wrapped = wrapped;
                this.first = first;
        }

        public boolean eval(Element el) {
                if (el == current)
                        return false;

                boolean b = wrapped.eval(el);
                if (link != null && b)
                        link.setMatched(el);
                return b;
        }

        public String toString() {
                if (first) {
                        StringBuilder sb = new StringBuilder();
                        sb.append('(');
                        sb.append(wrapped);
                        LinkedOp l = link;
                        while (l != null) {
                                sb.append(" | ");
                                sb.append(l.wrapped);
                                l = l.link;
                        }
                        sb.append(')');
                        return sb.toString();
                } else {
                        return "# Part of the previous OR expression.";
                }
        }

        public int priority() {
                return wrapped.priority();
        }

        public boolean hasHigherPriority(Op other) {
                return this.wrapped.hasHigherPriority(other);
        }

        public Op getFirst() {
                return wrapped.getFirst();
        }

        public void setFirst(Op first) {
                wrapped.setFirst(first);
        }

        public Op getSecond() {
                return null;
        }

        public NodeType getType() {
                return wrapped.getType();
        }

        public String value(Element el) {
                return wrapped.value(el);
        }

        public String getKeyValue() {
                throw new UnsupportedOperationException();
        }

        public boolean isType(NodeType value) {
                return wrapped.isType(value);
        }

        /**
         * Set the fact that the given element has been matched already.  If we
         * are called to evaluate and find that the element we are given
         * has already been matched by an earlier term in the OR then we just
         * return without doing anything.
         * @param el The element to mark as being matched.
         */

        private void setMatched(Element el) {
                this.current = el;
                if (link != null)
                        link.setMatched(el);
        }

        public void setLink(LinkedOp link) {
                if (this.link == null)
                        this.link = link;
                else
                        this.link.setLink(link);
        }

        /**
         * Create either a LinkedOp or a LinkedBinaryOp as appropriate
         * for the type of operation that is passed in.
         */

        public static LinkedOp create(Op op, boolean first) {
                if (op instanceof BinaryOp) {
                        return new LinkedBinaryOp((BinaryOp) op, first);
                } else {
                        return new LinkedOp(op, first);
                }
        }
}