Changeset 8494 in josm


Ignore:
Timestamp:
2015-06-19T19:18:10+02:00 (7 years ago)
Author:
simon04
Message:

see #11150 - MapCSS: refactor pseudo classes to factory based instantiation; consider :sameTags, :same-tags, :same_tags equivalent

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Condition.java

    r8338 r8494  
    22package org.openstreetmap.josm.gui.mappaint.mapcss;
    33
     4import java.lang.reflect.InvocationTargetException;
     5import java.lang.reflect.Method;
    46import java.text.MessageFormat;
    57import java.util.Arrays;
     
    6971
    7072    public static PseudoClassCondition createPseudoClassCondition(String id, boolean not, Context context) {
    71         return new PseudoClassCondition(id, not, context);
     73        return PseudoClassCondition.createPseudoClassCondition(id, not, context);
    7274    }
    7375
     
    376378    }
    377379
     380    /**
     381     * Like <a href="http://www.w3.org/TR/css3-selectors/#pseudo-classes">CSS pseudo classes</a>, MapCSS pseudo classes
     382     * are written in lower case with dashes between words.
     383     */
     384    static class PseudoClasses {
     385
     386        /**
     387         * {@code closed} tests whether the way is closed or the relation is a closed multipolygon
     388         */
     389        static boolean closed(Environment e) {
     390            if (e.osm instanceof Way && ((Way) e.osm).isClosed())
     391                return true;
     392            if (e.osm instanceof Relation && ((Relation) e.osm).isMultipolygon())
     393                return true;
     394            return false;
     395        }
     396
     397        /**
     398         * {@code :modified} tests whether the object has been modified.
     399         * @see OsmPrimitive#isModified() ()
     400         */
     401        static boolean modified(Environment e) {
     402            return e.osm.isModified() || e.osm.isNewOrUndeleted();
     403        }
     404
     405        /**
     406         * {@code ;new} tests whether the object is new.
     407         * @see OsmPrimitive#isNew()
     408         */
     409        static boolean _new(Environment e) {
     410            return e.osm.isNew();
     411        }
     412
     413        /**
     414         * {@code :connection} tests whether the object is a connection node.
     415         * @see Node#isConnectionNode()
     416         */
     417        static boolean connection(Environment e) {
     418            return e.osm instanceof Node && ((Node) e.osm).isConnectionNode();
     419        }
     420
     421        /**
     422         * {@code :tagged} tests whether the object is tagged.
     423         * @see OsmPrimitive#isTagged()
     424         */
     425        static boolean tagged(Environment e) {
     426            return e.osm.isTagged();
     427        }
     428
     429        /**
     430         * {@code :same-tags} tests whether the object has the same tags as its child/parent.
     431         * @see OsmPrimitive#hasSameInterestingTags(OsmPrimitive)
     432         */
     433        static boolean sameTags(Environment e) {
     434            return e.osm.hasSameInterestingTags(Utils.firstNonNull(e.child, e.parent));
     435        }
     436
     437        /**
     438         * {@code :area-style} tests whether the object has an area style. This is useful for validators.
     439         * @see ElemStyles#hasAreaElemStyle(OsmPrimitive, boolean)
     440         */
     441        static boolean areaStyle(Environment e) {
     442            // only for validator
     443            return ElemStyles.hasAreaElemStyle(e.osm, false);
     444        }
     445
     446        /**
     447         * {@code unconnected}: tests whether the object is a unconnected node.
     448         */
     449        static boolean unconnected(Environment e) {
     450            return e.osm instanceof Node && OsmPrimitive.getFilteredList(e.osm.getReferrers(), Way.class).isEmpty();
     451        }
     452
     453        /**
     454         * {@code righthandtraffic} checks if there is right-hand traffic at the current location.
     455         * @see ExpressionFactory.Functions#is_right_hand_traffic(Environment)
     456         */
     457        static boolean righthandtraffic(Environment e) {
     458            return ExpressionFactory.Functions.is_right_hand_traffic(e);
     459        }
     460
     461        /**
     462         * {@code unclosed-multipolygon} tests whether the object is an unclosed multipolygon.
     463         */
     464        static boolean unclosed_multipolygon(Environment e) {
     465            return e.osm instanceof Relation && ((Relation) e.osm).isMultipolygon() &&
     466                    !e.osm.isIncomplete() && !((Relation) e.osm).hasIncompleteMembers() &&
     467                    !MultipolygonCache.getInstance().get(Main.map.mapView, (Relation) e.osm).getOpenEnds().isEmpty();
     468        }
     469    }
     470
    378471    public static class PseudoClassCondition extends Condition {
    379472
    380         public final String id;
     473        public final Method method;
    381474        public final boolean not;
    382475
    383         public PseudoClassCondition(String id, boolean not, Context context) {
    384             this.id = id;
     476        private PseudoClassCondition(Method method, boolean not) {
     477            this.method = method;
    385478            this.not = not;
     479        }
     480
     481        public static PseudoClassCondition createPseudoClassCondition(String id, boolean not, Context context) {
    386482            CheckParameterUtil.ensureThat(!"sameTags".equals(id) || Context.LINK.equals(context), "sameTags only supported in LINK context");
     483            if ("open_end".equals(id)) {
     484                return new OpenEndPseudoClassCondition(not);
     485            }
     486            final Method method = getMethod(id);
     487            if (method != null) {
     488                return new PseudoClassCondition(method, not);
     489            }
     490            throw new IllegalArgumentException("Invalid pseudo class specified: " + id);
     491
     492        }
     493
     494        protected static Method getMethod(String id) {
     495            id = id.replaceAll("-|_", "");
     496            for (Method method : PseudoClasses.class.getDeclaredMethods()) {
     497                // for backwards compatibility, consider :sameTags == :same-tags == :same_tags (#11150)
     498                final String methodName = method.getName().replaceAll("-|_", "");
     499                if (methodName.equalsIgnoreCase(id)) {
     500                    return method;
     501                }
     502            }
     503            return null;
    387504        }
    388505
    389506        @Override
    390507        public boolean applies(Environment e) {
    391             return not ^ appliesImpl(e);
    392         }
    393 
    394         public boolean appliesImpl(Environment e) {
    395             switch(id) {
    396             case "closed":
    397                 if (e.osm instanceof Way && ((Way) e.osm).isClosed())
    398                     return true;
    399                 if (e.osm instanceof Relation && ((Relation) e.osm).isMultipolygon())
    400                     return true;
    401                 break;
    402             case "modified":
    403                 return e.osm.isModified() || e.osm.isNewOrUndeleted();
    404             case "new":
    405                 return e.osm.isNew();
    406             case "connection":
    407                 return e.osm instanceof Node && ((Node) e.osm).isConnectionNode();
    408             case "tagged":
    409                 return e.osm.isTagged();
    410             case "sameTags":
    411                 return e.osm.hasSameInterestingTags(Utils.firstNonNull(e.child, e.parent));
    412             case "areaStyle":
    413                 // only for validator
    414                 return ElemStyles.hasAreaElemStyle(e.osm, false);
    415             case "unconnected":
    416                 return e.osm instanceof Node && OsmPrimitive.getFilteredList(e.osm.getReferrers(), Way.class).isEmpty();
    417             case "righthandtraffic":
    418                 return ExpressionFactory.Functions.is_right_hand_traffic(e);
    419             case "unclosed_multipolygon":
    420                 return e.osm instanceof Relation && ((Relation) e.osm).isMultipolygon() &&
    421                         !e.osm.isIncomplete() && !((Relation) e.osm).hasIncompleteMembers() &&
    422                         !MultipolygonCache.getInstance().get(Main.map.mapView, (Relation) e.osm).getOpenEnds().isEmpty();
    423             case "open_end":
    424                 // handling at org.openstreetmap.josm.gui.mappaint.mapcss.Selector.ChildOrParentSelector.MultipolygonOpenEndFinder
    425                 return true;
    426             }
    427             return false;
     508            try {
     509                return not ^ (Boolean) method.invoke(null, e);
     510            } catch (IllegalAccessException | InvocationTargetException ex) {
     511                throw new RuntimeException(ex);
     512            }
    428513        }
    429514
    430515        @Override
    431516        public String toString() {
    432             return ":" + (not ? "!" : "") + id;
     517            return (not ? "!" : "") + ":" + method.getName();
     518        }
     519    }
     520
     521    public static class OpenEndPseudoClassCondition extends PseudoClassCondition {
     522        public OpenEndPseudoClassCondition(boolean not) {
     523            super(null, not);
     524        }
     525
     526        @Override
     527        public boolean applies(Environment e) {
     528            return true;
    433529        }
    434530    }
  • trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java

    r8415 r8494  
    369369            } else if (ChildOrParentSelectorType.CHILD.equals(type)
    370370                    && link.conds != null && !link.conds.isEmpty()
    371                     && link.conds.get(0) instanceof Condition.PseudoClassCondition
    372                     && "open_end".equals(((Condition.PseudoClassCondition) link.conds.get(0)).id)) {
     371                    && link.conds.get(0) instanceof Condition.OpenEndPseudoClassCondition) {
    373372                if (e.osm instanceof Node) {
    374373                    e.osm.visitReferrers(new MultipolygonOpenEndFinder(e));
  • trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParserTest.groovy

    r8415 r8494  
    5353        assert conditions.get(1) instanceof Condition.ClassCondition
    5454        assert conditions.get(2) instanceof Condition.PseudoClassCondition
     55        assert !conditions.get(2).applies(getEnvironment("name", "X"))
     56    }
     57
     58    @Test
     59    public void testPseudoClassCondition() throws Exception {
     60        def c1 = ((Selector.GeneralSelector) getParser("way!:area-style").selector()).conds.get(0)
     61        def c2 = ((Selector.GeneralSelector) getParser("way!:areaStyle").selector()).conds.get(0)
     62        def c3 = ((Selector.GeneralSelector) getParser("way!:area_style").selector()).conds.get(0)
     63        assert c1.toString() == "!:areaStyle"
     64        assert c2.toString() == "!:areaStyle"
     65        assert c3.toString() == "!:areaStyle"
    5566    }
    5667
Note: See TracChangeset for help on using the changeset viewer.