Changeset 3342 in josm for trunk


Ignore:
Timestamp:
2010-06-26T16:44:15+02:00 (14 years ago)
Author:
jttt
Message:

Cleanup/organize OsmPrimitive

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java

    r3341 r3342  
    184184    }
    185185
    186     /* mappaint data */
    187     public ElemStyle mappaintStyle = null;
    188     public int mappaintDrawnCode = 0;
    189 
    190     /* This should not be called from outside. Fixing the UI to add relevant
    191        get/set functions calling this implicitely is preferred, so we can have
    192        transparent cache handling in the future. */
    193     protected void clearCached()
    194     {
    195         mappaintDrawnCode = 0;
    196         mappaintStyle = null;
    197     }
    198     /* end of mappaint data */
    199 
    200     /**
    201      * Unique identifier in OSM. This is used to identify objects on the server.
    202      * An id of 0 means an unknown id. The object has not been uploaded yet to
    203      * know what id it will get.
    204      *
    205      */
    206     private long id = 0;
    207 
    208     /** the parent dataset */
    209     private DataSet dataSet;
    210 
    211     /**
    212      * This method should never ever by called from somewhere else than Dataset.addPrimitive or removePrimitive methods
    213      * @param dataSet
    214      */
    215     void setDataset(DataSet dataSet) {
    216         if (this.dataSet != null && dataSet != null && this.dataSet != dataSet)
    217             throw new DataIntegrityProblemException("Primitive cannot be included in more than one Dataset");
    218         this.dataSet = dataSet;
    219     }
    220 
    221     /**
    222      *
    223      * @return DataSet this primitive is part of.
    224      */
    225     public DataSet getDataSet() {
    226         return dataSet;
    227     }
    228 
    229     /**
    230      * Throws exception if primitive is not part of the dataset
    231      */
    232     public void checkDataset() {
    233         if (dataSet == null)
    234             throw new DataIntegrityProblemException("Primitive  must be part of the dataset: " + toString());
    235     }
    236 
    237     private volatile short flags = FLAG_VISIBLE;   // visible per default
    238 
    239     /**
    240      * User that last modified this primitive, as specified by the server.
    241      * Never changed by JOSM.
    242      */
    243     private User user = null;
    244 
    245     /**
    246      * Contains the version number as returned by the API. Needed to
    247      * ensure update consistency
    248      */
    249     private int version = 0;
    250 
    251     /**
    252      * The id of the changeset this primitive was last uploaded to.
    253      * 0 if it wasn't uploaded to a changeset yet of if the changeset
    254      * id isn't known.
    255      */
    256     private int changesetId;
     186    /**
     187     * Some predicates, that describe conditions on primitives.
     188     */
     189    public static final Predicate<OsmPrimitive> isUsablePredicate = new Predicate<OsmPrimitive>() {
     190        public boolean evaluate(OsmPrimitive primitive) {
     191            return primitive.isUsable();
     192        }
     193    };
     194
     195    public static final Predicate<OsmPrimitive> isSelectablePredicate = new Predicate<OsmPrimitive>() {
     196        public boolean evaluate(OsmPrimitive primitive) {
     197            return primitive.isSelectable();
     198        }
     199    };
     200
     201    public static final Predicate<OsmPrimitive> nonDeletedPredicate = new Predicate<OsmPrimitive>() {
     202        public boolean evaluate(OsmPrimitive primitive) {
     203            return !primitive.isDeleted();
     204        }
     205    };
     206
     207    public static final Predicate<OsmPrimitive> nonDeletedCompletePredicate = new Predicate<OsmPrimitive>() {
     208        public boolean evaluate(OsmPrimitive primitive) {
     209            return !primitive.isDeleted() && !primitive.isIncomplete();
     210        }
     211    };
     212
     213    public static final Predicate<OsmPrimitive> nonDeletedPhysicalPredicate = new Predicate<OsmPrimitive>() {
     214        public boolean evaluate(OsmPrimitive primitive) {
     215            return !primitive.isDeleted() && !primitive.isIncomplete() && !(primitive instanceof Relation);
     216        }
     217    };
     218
     219    public static final Predicate<OsmPrimitive> modifiedPredicate = new Predicate<OsmPrimitive>() {
     220        public boolean evaluate(OsmPrimitive primitive) {
     221            return primitive.isModified();
     222        }
     223    };
     224
     225    public static final Predicate<OsmPrimitive> nodePredicate = new Predicate<OsmPrimitive>() {
     226        public boolean evaluate(OsmPrimitive primitive) {
     227            return primitive.getClass() == Node.class;
     228        }
     229    };
     230
     231    public static final Predicate<OsmPrimitive> wayPredicate = new Predicate<OsmPrimitive>() {
     232        public boolean evaluate(OsmPrimitive primitive) {
     233            return primitive.getClass() == Way.class;
     234        }
     235    };
     236
     237    public static final Predicate<OsmPrimitive> relationPredicate = new Predicate<OsmPrimitive>() {
     238        public boolean evaluate(OsmPrimitive primitive) {
     239            return primitive.getClass() == Relation.class;
     240        }
     241    };
     242
     243    public static final Predicate<OsmPrimitive> allPredicate = new Predicate<OsmPrimitive>() {
     244        public boolean evaluate(OsmPrimitive primitive) {
     245            return true;
     246        }
     247    };
    257248
    258249    /**
     
    304295    }
    305296
    306     /* ------------------------------------------------------------------------------------ */
    307     /* accessors                                                                            */
    308     /* ------------------------------------------------------------------------------------ */
    309 
    310     /**
    311      * Make the primitive disabled (e.g. if a filter applies).
    312      * To enable the primitive again, use unsetDisabledState.
    313      * @param hide if the primitive should be completely hidden from view or
    314      *             just shown in gray color.
    315      */
    316     public void setDisabledState(boolean hide) {
    317         flags |= FLAG_DISABLED;
    318         if (hide) {
    319             flags |= FLAG_HIDE_IF_DISABLED;
    320         } else {
    321             flags &= ~FLAG_HIDE_IF_DISABLED;
    322         }
    323     }
    324 
    325     /**
    326      * Remove the disabled flag from the primitive.
    327      * Afterwards, the primitive is displayed normally and can be selected
    328      * again.
    329      */
    330     public void unsetDisabledState() {
    331         flags &= ~FLAG_DISABLED;
    332         flags &= ~FLAG_HIDE_IF_DISABLED;
    333     }
    334 
    335     /**
    336      * Replies true, if this primitive is disabled. (E.g. a filter
    337      * applies)
    338      */
    339     public boolean isDisabled() {
    340         return (flags & FLAG_DISABLED) != 0;
    341     }
    342 
    343     /**
    344      * Replies true, if this primitive is disabled and marked as
    345      * completely hidden on the map.
    346      */
    347     public boolean isDisabledAndHidden() {
    348         return (((flags & FLAG_DISABLED) != 0) && ((flags & FLAG_HIDE_IF_DISABLED) != 0));
    349     }
    350 
    351     @Deprecated
    352     public boolean isFiltered() {
    353         return isDisabledAndHidden();
    354     }
    355 
    356     /**
    357      * Marks this primitive as being modified.
    358      *
    359      * @param modified true, if this primitive is to be modified
    360      */
    361     public void setModified(boolean modified) {
    362         if (modified) {
    363             flags |= FLAG_MODIFIED;
    364         } else {
    365             flags &= ~FLAG_MODIFIED;
    366         }
    367     }
    368 
    369     /**
    370      * Replies <code>true</code> if the object has been modified since it was loaded from
    371      * the server. In this case, on next upload, this object will be updated.
    372      *
    373      * Deleted objects are deleted from the server. If the objects are added (id=0),
    374      * the modified is ignored and the object is added to the server.
    375      *
    376      * @return <code>true</code> if the object has been modified since it was loaded from
    377      * the server
    378      */
    379     public boolean isModified() {
    380         return (flags & FLAG_MODIFIED) != 0;
    381     }
    382 
    383     /**
    384      * Replies <code>true</code>, if the object has been deleted.
    385      *
    386      * @return <code>true</code>, if the object has been deleted.
    387      * @see #setDeleted(boolean)
    388      */
    389     public boolean isDeleted() {
    390         return (flags & FLAG_DELETED) != 0;
    391     }
    392 
    393     /**
    394      * Replies <code>true</code> if the object has been deleted on the server and was undeleted by the user.
    395      * @return <code>true</code> if the object has been undeleted
    396      */
    397     public boolean isUndeleted() {
    398         return (flags & (FLAG_VISIBLE + FLAG_DELETED)) == 0;
    399     }
    400 
    401     /**
    402      * Replies <code>true</code>, if the object is usable (i.e. complete
    403      * and not deleted).
    404      *
    405      * @return <code>true</code>, if the object is usable.
    406      * @see #delete(boolean)
    407      */
    408     public boolean isUsable() {
    409         return (flags & (FLAG_DELETED + FLAG_INCOMPLETE)) == 0;
    410     }
    411 
    412     public boolean isSelectable() {
    413         return (flags & (FLAG_DELETED + FLAG_INCOMPLETE + FLAG_DISABLED + FLAG_HIDE_IF_DISABLED)) == 0;
    414     }
    415 
    416     public boolean isDrawable() {
    417         return (flags & (FLAG_DELETED + FLAG_INCOMPLETE + FLAG_HIDE_IF_DISABLED)) == 0;
    418     }
    419 
    420     /**
    421      * Some predicates, that describe conditions on primitives.
    422      */
    423     public static Predicate<OsmPrimitive> isUsablePredicate = new Predicate<OsmPrimitive>() {
    424         public boolean evaluate(OsmPrimitive primitive) {
    425             return primitive.isUsable();
    426         }
    427     };
    428 
    429     public static Predicate<OsmPrimitive> isSelectablePredicate = new Predicate<OsmPrimitive>() {
    430         public boolean evaluate(OsmPrimitive primitive) {
    431             return primitive.isSelectable();
    432         }
    433     };
    434 
    435     public static Predicate<OsmPrimitive> nonDeletedPredicate = new Predicate<OsmPrimitive>() {
    436         public boolean evaluate(OsmPrimitive primitive) {
    437             return !primitive.isDeleted();
    438         }
    439     };
    440 
    441     public static Predicate<OsmPrimitive> nonDeletedCompletePredicate = new Predicate<OsmPrimitive>() {
    442         public boolean evaluate(OsmPrimitive primitive) {
    443             return !primitive.isDeleted() && !primitive.isIncomplete();
    444         }
    445     };
    446 
    447     public static Predicate<OsmPrimitive> nonDeletedPhysicalPredicate = new Predicate<OsmPrimitive>() {
    448         public boolean evaluate(OsmPrimitive primitive) {
    449             return !primitive.isDeleted() && !primitive.isIncomplete() && !(primitive instanceof Relation);
    450         }
    451     };
    452 
    453     public static Predicate<OsmPrimitive> modifiedPredicate = new Predicate<OsmPrimitive>() {
    454         public boolean evaluate(OsmPrimitive primitive) {
    455             return primitive.isModified();
    456         }
    457     };
    458 
    459     public static Predicate<OsmPrimitive> nodePredicate = new Predicate<OsmPrimitive>() {
    460         public boolean evaluate(OsmPrimitive primitive) {
    461             return primitive.getClass() == Node.class;
    462         }
    463     };
    464 
    465     public static Predicate<OsmPrimitive> wayPredicate = new Predicate<OsmPrimitive>() {
    466         public boolean evaluate(OsmPrimitive primitive) {
    467             return primitive.getClass() == Way.class;
    468         }
    469     };
    470 
    471     public static Predicate<OsmPrimitive> relationPredicate = new Predicate<OsmPrimitive>() {
    472         public boolean evaluate(OsmPrimitive primitive) {
    473             return primitive.getClass() == Relation.class;
    474         }
    475     };
    476 
    477     public static Predicate<OsmPrimitive> allPredicate = new Predicate<OsmPrimitive>() {
    478         public boolean evaluate(OsmPrimitive primitive) {
    479             return true;
    480         }
    481     };
    482 
    483 
    484     /**
    485      * Replies true if this primitive is either unknown to the server (i.e. its id
    486      * is 0) or it is known to the server and it hasn't be deleted on the server.
    487      * Replies false, if this primitive is known on the server and has been deleted
    488      * on the server.
    489      *
    490      * @see #setVisible(boolean)
    491      */
    492     public boolean isVisible() {
    493         return (flags & FLAG_VISIBLE) != 0;
    494     }
    495 
    496     /**
    497      * Sets whether this primitive is visible, i.e. whether it is known on the server
    498      * and not deleted on the server.
    499      *
    500      * @see #isVisible()
    501      * @throws IllegalStateException thrown if visible is set to false on an primitive with
    502      * id==0
    503      */
    504     public void setVisible(boolean visible) throws IllegalStateException{
    505         if (isNew() && visible == false)
    506             throw new IllegalStateException(tr("A primitive with ID = 0 cannot be invisible."));
    507         if (visible) {
    508             flags |= FLAG_VISIBLE;
    509         } else {
    510             flags &= ~FLAG_VISIBLE;
    511         }
    512     }
     297
     298    /*----------
     299     * MAPPAINT
     300     *--------*/
     301    public ElemStyle mappaintStyle = null;
     302    public int mappaintDrawnCode = 0;
     303
     304    /* This should not be called from outside. Fixing the UI to add relevant
     305       get/set functions calling this implicitely is preferred, so we can have
     306       transparent cache handling in the future. */
     307    protected void clearCached()
     308    {
     309        mappaintDrawnCode = 0;
     310        mappaintStyle = null;
     311    }
     312    /* end of mappaint data */
     313
     314    /*---------
     315     * DATASET
     316     *---------*/
     317
     318    /** the parent dataset */
     319    private DataSet dataSet;
     320
     321    /**
     322     * This method should never ever by called from somewhere else than Dataset.addPrimitive or removePrimitive methods
     323     * @param dataSet
     324     */
     325    void setDataset(DataSet dataSet) {
     326        if (this.dataSet != null && dataSet != null && this.dataSet != dataSet)
     327            throw new DataIntegrityProblemException("Primitive cannot be included in more than one Dataset");
     328        this.dataSet = dataSet;
     329    }
     330
     331    /**
     332     *
     333     * @return DataSet this primitive is part of.
     334     */
     335    public DataSet getDataSet() {
     336        return dataSet;
     337    }
     338
     339    /**
     340     * Throws exception if primitive is not part of the dataset
     341     */
     342    public void checkDataset() {
     343        if (dataSet == null)
     344            throw new DataIntegrityProblemException("Primitive  must be part of the dataset: " + toString());
     345    }
     346
     347    /*-------------------
     348     * OTHER PROPERTIES
     349     *-------------------/
     350
     351    /**
     352     * Unique identifier in OSM. This is used to identify objects on the server.
     353     * An id of 0 means an unknown id. The object has not been uploaded yet to
     354     * know what id it will get.
     355     *
     356     */
     357    private long id = 0;
     358
     359    /**
     360     * User that last modified this primitive, as specified by the server.
     361     * Never changed by JOSM.
     362     */
     363    private User user = null;
     364
     365    /**
     366     * Contains the version number as returned by the API. Needed to
     367     * ensure update consistency
     368     */
     369    private int version = 0;
     370
     371    /**
     372     * The id of the changeset this primitive was last uploaded to.
     373     * 0 if it wasn't uploaded to a changeset yet of if the changeset
     374     * id isn't known.
     375     */
     376    private int changesetId;
    513377
    514378    /**
     
    604468    }
    605469
    606     public void setTimestamp(Date timestamp) {
    607         this.timestamp = (int)(timestamp.getTime() / 1000);
    608     }
    609 
    610     /**
    611      * Time of last modification to this object. This is not set by JOSM but
    612      * read from the server and delivered back to the server unmodified. It is
    613      * used to check against edit conflicts.
    614      *
    615      */
    616     public Date getTimestamp() {
    617         return new Date(timestamp * 1000l);
    618     }
    619 
    620     public boolean isTimestampEmpty() {
    621         return timestamp == 0;
    622     }
    623 
    624     private int timestamp;
    625 
    626     private static volatile Collection<String> uninteresting = null;
    627     /**
    628      * Contains a list of "uninteresting" keys that do not make an object
    629      * "tagged".  Entries that end with ':' are causing a whole namespace to be considered
    630      * "uninteresting".  Only the first level namespace is considered.
    631      * Initialized by isUninterestingKey()
    632      */
    633     public static Collection<String> getUninterestingKeys() {
    634         if (uninteresting == null) {
    635             uninteresting = Main.pref.getCollection("tags.uninteresting",
    636                     Arrays.asList(new String[]{"source", "source_ref", "source:", "note", "comment",
    637                             "converted_by", "created_by", "watch", "watch:", "fixme", "FIXME",
    638                             "description"}));
    639         }
    640         return uninteresting;
    641     }
    642 
    643     /**
    644      * Returns true if key is considered "uninteresting".
    645      */
    646     public static boolean isUninterestingKey(String key) {
    647         getUninterestingKeys();
    648         if (uninteresting.contains(key))
    649             return true;
    650         int pos = key.indexOf(':');
    651         if (pos > 0)
    652             return uninteresting.contains(key.substring(0, pos + 1));
    653         return false;
    654     }
    655 
    656     private static volatile Match directionKeys = null;
    657     private static volatile Match reversedDirectionKeys = null;
    658 
    659     /**
    660      * Contains a list of direction-dependent keys that make an object
    661      * direction dependent.
    662      * Initialized by checkDirectionTagged()
    663      */
    664     static {
    665         // Legacy support - convert list of keys to search pattern
    666         if (Main.pref.isCollection("tags.direction", false)) {
    667             System.out.println("Collection of keys in tags.direction is no longer supported, value will converted to search pattern");
    668             Collection<String> keys = Main.pref.getCollection("tags.direction", null);
    669             StringBuilder builder = new StringBuilder();
    670             for (String key:keys) {
    671                 builder.append(key);
    672                 builder.append("=* | ");
    673             }
    674             builder.delete(builder.length() - 3, builder.length());
    675             Main.pref.put("tags.direction", builder.toString());
    676         }
    677 
    678         // FIXME: incline=\"-*\" search pattern does not work.
    679         String reversedDirectionDefault = "oneway=\"-1\" | incline=down | incline=\"-*\"";
    680 
    681         String directionDefault = "oneway? | incline=* | aerialway=* | "+
    682         "waterway=stream | waterway=river | waterway=canal | waterway=drain | waterway=rapids | "+
    683         "\"piste:type\"=downhill | \"piste:type\"=sled | man_made=\"piste:halfpipe\" | "+
    684         "junction=roundabout";
    685 
    686         try {
    687             reversedDirectionKeys = SearchCompiler.compile(Main.pref.get("tags.reversed_direction", reversedDirectionDefault), false, false);
    688         } catch (ParseError e) {
    689             System.err.println("Unable to compile pattern for tags.reversed_direction, trying default pattern: " + e.getMessage());
    690 
    691             try {
    692                 reversedDirectionKeys = SearchCompiler.compile(reversedDirectionDefault, false, false);
    693             } catch (ParseError e2) {
    694                 throw new AssertionError("Unable to compile default pattern for direction keys: " + e2.getMessage());
    695             }
    696         }
    697         try {
    698             directionKeys = SearchCompiler.compile(Main.pref.get("tags.direction", directionDefault), false, false);
    699         } catch (ParseError e) {
    700             System.err.println("Unable to compile pattern for tags.direction, trying default pattern: " + e.getMessage());
    701 
    702             try {
    703                 directionKeys = SearchCompiler.compile(directionDefault, false, false);
    704             } catch (ParseError e2) {
    705                 throw new AssertionError("Unable to compile default pattern for direction keys: " + e2.getMessage());
    706             }
    707         }
    708     }
    709 
    710     /**
    711      * Replies a list of direction-dependent keys that make an object
    712      * direction dependent.
    713      *
    714      * @return  a list of direction-dependent keys that make an object
    715      * direction dependent.
    716      */
    717     @Deprecated
    718     public static Collection<String> getDirectionKeys() {
    719         return Main.pref.getCollection("tags.direction",
    720                 Arrays.asList("oneway","incline","incline_steep","aerialway"));
    721     }
    722 
    723     /**
    724      * Implementation of the visitor scheme. Subclasses have to call the correct
    725      * visitor function.
    726      * @param visitor The visitor from which the visit() function must be called.
    727      */
    728     abstract public void visit(Visitor visitor);
    729 
    730     /**
    731      * Sets whether this primitive is deleted or not.
    732      *
    733      * Also marks this primitive as modified if deleted is true.
    734      *
    735      * @param deleted  true, if this primitive is deleted; false, otherwise
    736      */
    737     public void setDeleted(boolean deleted) {
    738         if (deleted) {
    739             flags |= FLAG_DELETED;
    740         } else {
    741             flags &= ~FLAG_DELETED;
    742         }
    743         setModified(deleted ^ !isVisible());
    744         if (dataSet != null) {
    745             if (deleted) {
    746                 dataSet.firePrimitivesRemoved(Collections.singleton(this), false);
    747             } else {
    748                 dataSet.firePrimitivesAdded(Collections.singleton(this), false);
    749             }
    750         }
    751     }
    752 
    753470    /**
    754471     * Replies the user who has last touched this object. May be null.
     
    803520
    804521    /**
    805      * Equal, if the id (and class) is equal.
    806      *
    807      * An primitive is equal to its incomplete counter part.
    808      */
    809     @Override public boolean equals(Object obj) {
    810         if (obj instanceof OsmPrimitive)
    811             return ((OsmPrimitive)obj).id == id && obj.getClass() == getClass();
     522     * Replies the unique primitive id for this primitive
     523     *
     524     * @return the unique primitive id for this primitive
     525     */
     526    public PrimitiveId getPrimitiveId() {
     527        return new SimplePrimitiveId(getUniqueId(), getType());
     528    }
     529
     530    public void setTimestamp(Date timestamp) {
     531        this.timestamp = (int)(timestamp.getTime() / 1000);
     532    }
     533
     534    /**
     535     * Time of last modification to this object. This is not set by JOSM but
     536     * read from the server and delivered back to the server unmodified. It is
     537     * used to check against edit conflicts.
     538     *
     539     */
     540    public Date getTimestamp() {
     541        return new Date(timestamp * 1000l);
     542    }
     543
     544    public boolean isTimestampEmpty() {
     545        return timestamp == 0;
     546    }
     547
     548    private int timestamp;
     549
     550    /* -------
     551    /* FLAGS
     552    /* ------*/
     553
     554    private volatile short flags = FLAG_VISIBLE;   // visible per default
     555
     556    private void updateFlags(int flag, boolean value) {
     557        if (value) {
     558            flags |= flag;
     559        } else {
     560            flags &= ~flag;
     561        }
     562    }
     563
     564    /**
     565     * Make the primitive disabled (e.g. if a filter applies).
     566     * To enable the primitive again, use unsetDisabledState.
     567     * @param hide if the primitive should be completely hidden from view or
     568     *             just shown in gray color.
     569     */
     570    public void setDisabledState(boolean hide) {
     571        flags |= FLAG_DISABLED;
     572        if (hide) {
     573            flags |= FLAG_HIDE_IF_DISABLED;
     574        } else {
     575            flags &= ~FLAG_HIDE_IF_DISABLED;
     576        }
     577    }
     578
     579    /**
     580     * Remove the disabled flag from the primitive.
     581     * Afterwards, the primitive is displayed normally and can be selected
     582     * again.
     583     */
     584    public void unsetDisabledState() {
     585        updateFlags(FLAG_DISABLED + FLAG_HIDE_IF_DISABLED, false);
     586    }
     587
     588    /**
     589     * Replies true, if this primitive is disabled. (E.g. a filter
     590     * applies)
     591     */
     592    public boolean isDisabled() {
     593        return (flags & FLAG_DISABLED) != 0;
     594    }
     595
     596    /**
     597     * Replies true, if this primitive is disabled and marked as
     598     * completely hidden on the map.
     599     */
     600    public boolean isDisabledAndHidden() {
     601        return (((flags & FLAG_DISABLED) != 0) && ((flags & FLAG_HIDE_IF_DISABLED) != 0));
     602    }
     603
     604    @Deprecated
     605    public boolean isFiltered() {
     606        return isDisabledAndHidden();
     607    }
     608
     609    /**
     610     * Marks this primitive as being modified.
     611     *
     612     * @param modified true, if this primitive is to be modified
     613     */
     614    public void setModified(boolean modified) {
     615        updateFlags(FLAG_MODIFIED, modified);
     616    }
     617
     618    /**
     619     * Replies <code>true</code> if the object has been modified since it was loaded from
     620     * the server. In this case, on next upload, this object will be updated.
     621     *
     622     * Deleted objects are deleted from the server. If the objects are added (id=0),
     623     * the modified is ignored and the object is added to the server.
     624     *
     625     * @return <code>true</code> if the object has been modified since it was loaded from
     626     * the server
     627     */
     628    public boolean isModified() {
     629        return (flags & FLAG_MODIFIED) != 0;
     630    }
     631
     632    /**
     633     * Replies <code>true</code>, if the object has been deleted.
     634     *
     635     * @return <code>true</code>, if the object has been deleted.
     636     * @see #setDeleted(boolean)
     637     */
     638    public boolean isDeleted() {
     639        return (flags & FLAG_DELETED) != 0;
     640    }
     641
     642    /**
     643     * Replies <code>true</code> if the object has been deleted on the server and was undeleted by the user.
     644     * @return <code>true</code> if the object has been undeleted
     645     */
     646    public boolean isUndeleted() {
     647        return (flags & (FLAG_VISIBLE + FLAG_DELETED)) == 0;
     648    }
     649
     650    /**
     651     * Replies <code>true</code>, if the object is usable (i.e. complete
     652     * and not deleted).
     653     *
     654     * @return <code>true</code>, if the object is usable.
     655     * @see #delete(boolean)
     656     */
     657    public boolean isUsable() {
     658        return (flags & (FLAG_DELETED + FLAG_INCOMPLETE)) == 0;
     659    }
     660
     661    public boolean isSelectable() {
     662        return (flags & (FLAG_DELETED + FLAG_INCOMPLETE + FLAG_DISABLED + FLAG_HIDE_IF_DISABLED)) == 0;
     663    }
     664
     665    public boolean isDrawable() {
     666        return (flags & (FLAG_DELETED + FLAG_INCOMPLETE + FLAG_HIDE_IF_DISABLED)) == 0;
     667    }
     668
     669    /**
     670     * Replies true if this primitive is either unknown to the server (i.e. its id
     671     * is 0) or it is known to the server and it hasn't be deleted on the server.
     672     * Replies false, if this primitive is known on the server and has been deleted
     673     * on the server.
     674     *
     675     * @see #setVisible(boolean)
     676     */
     677    public boolean isVisible() {
     678        return (flags & FLAG_VISIBLE) != 0;
     679    }
     680
     681    /**
     682     * Sets whether this primitive is visible, i.e. whether it is known on the server
     683     * and not deleted on the server.
     684     *
     685     * @see #isVisible()
     686     * @throws IllegalStateException thrown if visible is set to false on an primitive with
     687     * id==0
     688     */
     689    public void setVisible(boolean visible) throws IllegalStateException{
     690        if (isNew() && visible == false)
     691            throw new IllegalStateException(tr("A primitive with ID = 0 cannot be invisible."));
     692        updateFlags(FLAG_VISIBLE, visible);
     693    }
     694
     695    /**
     696     * Sets whether this primitive is deleted or not.
     697     *
     698     * Also marks this primitive as modified if deleted is true.
     699     *
     700     * @param deleted  true, if this primitive is deleted; false, otherwise
     701     */
     702    public void setDeleted(boolean deleted) {
     703        updateFlags(FLAG_DELETED, deleted);
     704        setModified(deleted ^ !isVisible());
     705        if (dataSet != null) {
     706            if (deleted) {
     707                dataSet.firePrimitivesRemoved(Collections.singleton(this), false);
     708            } else {
     709                dataSet.firePrimitivesAdded(Collections.singleton(this), false);
     710            }
     711        }
     712    }
     713
     714
     715    /**
     716     * If set to true, this object is incomplete, which means only the id
     717     * and type is known (type is the objects instance class)
     718     */
     719    private void setIncomplete(boolean incomplete) {
     720        if (dataSet != null && incomplete != this.isIncomplete()) {
     721            if (incomplete) {
     722                dataSet.firePrimitivesRemoved(Collections.singletonList(this), true);
     723            } else {
     724                dataSet.firePrimitivesAdded(Collections.singletonList(this), true);
     725            }
     726        }
     727        updateFlags(FLAG_INCOMPLETE, incomplete);
     728    }
     729
     730    public boolean isIncomplete() {
     731        return (flags & FLAG_INCOMPLETE) != 0;
     732    }
     733
     734    public boolean isSelected() {
     735        return dataSet != null && dataSet.isSelected(this);
     736    }
     737
     738    public void setHighlighted(boolean highlighted) {
     739        if (isHighlighted() != highlighted) {
     740            updateFlags(FLAG_HIGHLIGHTED, highlighted);
     741            if (dataSet != null) {
     742                dataSet.fireHighlightingChanged(this);
     743            }
     744        }
     745    }
     746
     747    public boolean isHighlighted() {
     748        return (flags & FLAG_HIGHLIGHTED) != 0;
     749    }
     750
     751    /*----------------------------------
     752     * UNINTERESTING AND DIRECTION KEYS
     753     *----------------------------------*/
     754
     755
     756    private static volatile Collection<String> uninteresting = null;
     757    /**
     758     * Contains a list of "uninteresting" keys that do not make an object
     759     * "tagged".  Entries that end with ':' are causing a whole namespace to be considered
     760     * "uninteresting".  Only the first level namespace is considered.
     761     * Initialized by isUninterestingKey()
     762     */
     763    public static Collection<String> getUninterestingKeys() {
     764        if (uninteresting == null) {
     765            uninteresting = Main.pref.getCollection("tags.uninteresting",
     766                    Arrays.asList(new String[]{"source", "source_ref", "source:", "note", "comment",
     767                            "converted_by", "created_by", "watch", "watch:", "fixme", "FIXME",
     768                    "description"}));
     769        }
     770        return uninteresting;
     771    }
     772
     773    /**
     774     * Returns true if key is considered "uninteresting".
     775     */
     776    public static boolean isUninterestingKey(String key) {
     777        getUninterestingKeys();
     778        if (uninteresting.contains(key))
     779            return true;
     780        int pos = key.indexOf(':');
     781        if (pos > 0)
     782            return uninteresting.contains(key.substring(0, pos + 1));
    812783        return false;
    813784    }
    814785
    815     /**
    816      * Return the id plus the class type encoded as hashcode or super's hashcode if id is 0.
    817      *
    818      * An primitive has the same hashcode as its incomplete counterpart.
    819      */
    820     @Override public final int hashCode() {
    821         return (int)id;
     786    private static volatile Match directionKeys = null;
     787    private static volatile Match reversedDirectionKeys = null;
     788
     789    /**
     790     * Contains a list of direction-dependent keys that make an object
     791     * direction dependent.
     792     * Initialized by checkDirectionTagged()
     793     */
     794    static {
     795        // Legacy support - convert list of keys to search pattern
     796        if (Main.pref.isCollection("tags.direction", false)) {
     797            System.out.println("Collection of keys in tags.direction is no longer supported, value will converted to search pattern");
     798            Collection<String> keys = Main.pref.getCollection("tags.direction", null);
     799            StringBuilder builder = new StringBuilder();
     800            for (String key:keys) {
     801                builder.append(key);
     802                builder.append("=* | ");
     803            }
     804            builder.delete(builder.length() - 3, builder.length());
     805            Main.pref.put("tags.direction", builder.toString());
     806        }
     807
     808        // FIXME: incline=\"-*\" search pattern does not work.
     809        String reversedDirectionDefault = "oneway=\"-1\" | incline=down | incline=\"-*\"";
     810
     811        String directionDefault = "oneway? | incline=* | aerialway=* | "+
     812        "waterway=stream | waterway=river | waterway=canal | waterway=drain | waterway=rapids | "+
     813        "\"piste:type\"=downhill | \"piste:type\"=sled | man_made=\"piste:halfpipe\" | "+
     814        "junction=roundabout";
     815
     816        try {
     817            reversedDirectionKeys = SearchCompiler.compile(Main.pref.get("tags.reversed_direction", reversedDirectionDefault), false, false);
     818        } catch (ParseError e) {
     819            System.err.println("Unable to compile pattern for tags.reversed_direction, trying default pattern: " + e.getMessage());
     820
     821            try {
     822                reversedDirectionKeys = SearchCompiler.compile(reversedDirectionDefault, false, false);
     823            } catch (ParseError e2) {
     824                throw new AssertionError("Unable to compile default pattern for direction keys: " + e2.getMessage());
     825            }
     826        }
     827        try {
     828            directionKeys = SearchCompiler.compile(Main.pref.get("tags.direction", directionDefault), false, false);
     829        } catch (ParseError e) {
     830            System.err.println("Unable to compile pattern for tags.direction, trying default pattern: " + e.getMessage());
     831
     832            try {
     833                directionKeys = SearchCompiler.compile(directionDefault, false, false);
     834            } catch (ParseError e2) {
     835                throw new AssertionError("Unable to compile default pattern for direction keys: " + e2.getMessage());
     836            }
     837        }
     838    }
     839
     840    private void updateTagged() {
     841        if (keys != null) {
     842            for (String key: keySet()) {
     843                if (!isUninterestingKey(key)) {
     844                    updateFlags(FLAG_TAGGED, true);
     845                    return;
     846                }
     847            }
     848        }
     849        updateFlags(FLAG_TAGGED, false);
     850    }
     851
     852    /**
     853     * true if this object is considered "tagged". To be "tagged", an object
     854     * must have one or more "interesting" tags. "created_by" and "source"
     855     * are typically considered "uninteresting" and do not make an object
     856     * "tagged".
     857     */
     858    public boolean isTagged() {
     859        return (flags & FLAG_TAGGED) != 0;
     860    }
     861
     862    private void updateDirectionFlags() {
     863        boolean hasDirections = false;
     864        boolean directionReversed = false;
     865        if (reversedDirectionKeys.match(this)) {
     866            hasDirections = true;
     867            directionReversed = true;
     868        }
     869        if (directionKeys.match(this)) {
     870            hasDirections = true;
     871        }
     872
     873        updateFlags(FLAG_DIRECTION_REVERSED, directionReversed);
     874        updateFlags(FLAG_HAS_DIRECTIONS, hasDirections);
     875    }
     876
     877    /**
     878     * true if this object has direction dependent tags (e.g. oneway)
     879     */
     880    public boolean hasDirectionKeys() {
     881        return (flags & FLAG_HAS_DIRECTIONS) != 0;
     882    }
     883
     884    public boolean reversedDirection() {
     885        return (flags & FLAG_DIRECTION_REVERSED) != 0;
    822886    }
    823887
     
    11211185    }
    11221186
     1187    /*-----------------
     1188     * OTHER METHODS
     1189     *----------------/
     1190
     1191    /**
     1192     * Implementation of the visitor scheme. Subclasses have to call the correct
     1193     * visitor function.
     1194     * @param visitor The visitor from which the visit() function must be called.
     1195     */
     1196    abstract public void visit(Visitor visitor);
     1197
     1198
    11231199    /**
    11241200     * Get and write all attributes from the parameter. Does not fire any listener, so
     
    12301306    }
    12311307
    1232     private void updateTagged() {
    1233         if (keys != null) {
    1234             for (String key: keySet()) {
    1235                 if (!isUninterestingKey(key)) {
    1236                     flags |= FLAG_TAGGED;
    1237                     return;
    1238                 }
    1239             }
    1240         }
    1241         flags &= ~FLAG_TAGGED;
    1242     }
    1243 
    1244     /**
    1245      * true if this object is considered "tagged". To be "tagged", an object
    1246      * must have one or more "interesting" tags. "created_by" and "source"
    1247      * are typically considered "uninteresting" and do not make an object
    1248      * "tagged".
    1249      */
    1250     public boolean isTagged() {
    1251         return (flags & FLAG_TAGGED) != 0;
    1252     }
    1253 
    1254     private void updateDirectionFlags() {
    1255         boolean hasDirections = false;
    1256         boolean directionReversed = false;
    1257         if (reversedDirectionKeys.match(this)) {
    1258             hasDirections = true;
    1259             directionReversed = true;
    1260         }
    1261         if (directionKeys.match(this)) {
    1262             hasDirections = true;
    1263         }
    1264 
    1265         if (directionReversed) {
    1266             flags |= FLAG_DIRECTION_REVERSED;
    1267         } else {
    1268             flags &= ~FLAG_DIRECTION_REVERSED;
    1269         }
    1270         if (hasDirections) {
    1271             flags |= FLAG_HAS_DIRECTIONS;
    1272         } else {
    1273             flags &= ~FLAG_HAS_DIRECTIONS;
    1274         }
    1275     }
    1276 
    1277     /**
    1278      * true if this object has direction dependent tags (e.g. oneway)
    1279      */
    1280     public boolean hasDirectionKeys() {
    1281         return (flags & FLAG_HAS_DIRECTIONS) != 0;
    1282     }
    1283 
    1284     public boolean reversedDirection() {
    1285         return (flags & FLAG_DIRECTION_REVERSED) != 0;
    1286     }
    12871308    /**
    12881309     * Replies the name of this primitive. The default implementation replies the value
     
    13661387    }
    13671388
     1389
     1390    public abstract BBox getBBox();
     1391
     1392    /**
     1393     * Called by Dataset to update cached position information of primitive (bbox, cached EarthNorth, ...)
     1394     */
     1395    public abstract void updatePosition();
     1396
     1397    /*----------------
     1398     * OBJECT METHODS
     1399     *---------------*/
     1400
    13681401    protected String getFlagsAsString() {
    13691402        StringBuilder builder = new StringBuilder();
     
    14001433    }
    14011434
    1402     public abstract BBox getBBox();
    1403 
    1404     /**
    1405      * Called by Dataset to update cached position information of primitive (bbox, cached EarthNorth, ...)
    1406      */
    1407     public abstract void updatePosition();
    1408 
    1409     /**
    1410      * Replies the unique primitive id for this primitive
    1411      *
    1412      * @return the unique primitive id for this primitive
    1413      */
    1414     public PrimitiveId getPrimitiveId() {
    1415         return new SimplePrimitiveId(getUniqueId(), getType());
    1416     }
    1417 
    1418     /**
    1419      * If set to true, this object is incomplete, which means only the id
    1420      * and type is known (type is the objects instance class)
    1421      */
    1422     private void setIncomplete(boolean incomplete) {
    1423         if (dataSet != null && incomplete != this.isIncomplete()) {
    1424             if (incomplete) {
    1425                 dataSet.firePrimitivesRemoved(Collections.singletonList(this), true);
    1426             } else {
    1427                 dataSet.firePrimitivesAdded(Collections.singletonList(this), true);
    1428             }
    1429         }
    1430         if (incomplete) {
    1431             flags |= FLAG_INCOMPLETE;
    1432         } else {
    1433             flags &= ~FLAG_INCOMPLETE;
    1434         }
    1435     }
    1436 
    1437     public boolean isIncomplete() {
    1438         return (flags & FLAG_INCOMPLETE) != 0;
    1439     }
    1440 
    1441     public boolean isSelected() {
    1442         return dataSet != null && dataSet.isSelected(this);
    1443     }
    1444 
    1445     public void setHighlighted(boolean highlighted) {
    1446         if (isHighlighted() != highlighted) {
    1447             if (highlighted) {
    1448                 flags |= FLAG_HIGHLIGHTED;
    1449             } else {
    1450                 flags &= ~FLAG_HIGHLIGHTED;
    1451             }
    1452             if (dataSet != null) {
    1453                 dataSet.fireHighlightingChanged(this);
    1454             }
    1455         }
    1456     }
    1457 
    1458     public boolean isHighlighted() {
    1459         return (flags & FLAG_HIGHLIGHTED) != 0;
    1460     }
     1435    /**
     1436     * Equal, if the id (and class) is equal.
     1437     *
     1438     * An primitive is equal to its incomplete counter part.
     1439     */
     1440    @Override public boolean equals(Object obj) {
     1441        if (obj instanceof OsmPrimitive)
     1442            return ((OsmPrimitive)obj).id == id && obj.getClass() == getClass();
     1443        return false;
     1444    }
     1445
     1446    /**
     1447     * Return the id plus the class type encoded as hashcode or super's hashcode if id is 0.
     1448     *
     1449     * An primitive has the same hashcode as its incomplete counterpart.
     1450     */
     1451    @Override public final int hashCode() {
     1452        return (int)id;
     1453    }
     1454
    14611455}
Note: See TracChangeset for help on using the changeset viewer.