source: josm/trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java@ 2309

Last change on this file since 2309 was 2309, checked in by stoecker, 15 years ago

applied #3696 - patch by Dave - removed primitive based selection

  • Property svn:eol-style set to native
File size: 25.2 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.data.osm;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.util.ArrayList;
7import java.util.Arrays;
8import java.util.Collection;
9import java.util.Collections;
10import java.util.Date;
11import java.util.HashMap;
12import java.util.HashSet;
13import java.util.LinkedList;
14import java.util.List;
15import java.util.Locale;
16import java.util.Map;
17import java.util.Set;
18import java.util.Map.Entry;
19import java.util.concurrent.atomic.AtomicLong;
20
21import org.openstreetmap.josm.Main;
22import org.openstreetmap.josm.data.osm.visitor.Visitor;
23import org.openstreetmap.josm.gui.mappaint.ElemStyle;
24
25
26/**
27 * An OSM primitive can be associated with a key/value pair. It can be created, deleted
28 * and updated within the OSM-Server.
29 *
30 * Although OsmPrimitive is designed as a base class, it is not to be meant to subclass
31 * it by any other than from the package {@link org.openstreetmap.josm.data.osm}. The available primitives are a fixed set that are given
32 * by the server environment and not an extendible data stuff.
33 *
34 * @author imi
35 */
36abstract public class OsmPrimitive implements Comparable<OsmPrimitive>, Tagged {
37
38 private static final AtomicLong idCounter = new AtomicLong(0);
39
40 static long generateUniqueId() {
41 return idCounter.decrementAndGet();
42 }
43
44 private static final int FLAG_MODIFIED = 1 << 0;
45 private static final int FLAG_VISIBLE = 1 << 1;
46 private static final int FLAG_DISABLED = 1 << 2;
47 private static final int FLAG_DELETED = 1 << 3;
48 private static final int FLAG_FILTERED = 1 << 4;
49 private static final int FLAG_HAS_DIRECTIONS = 1 << 5;
50 private static final int FLAG_TAGGED = 1 << 6;
51
52 /**
53 * Replies the sub-collection of {@see OsmPrimitive}s of type <code>type</code> present in
54 * another collection of {@see OsmPrimitive}s. The result collection is a list.
55 *
56 * If <code>list</code> is null, replies an empty list.
57 *
58 * @param <T>
59 * @param list the original list
60 * @param type the type to filter for
61 * @return the sub-list of OSM primitives of type <code>type</code>
62 */
63 static public <T extends OsmPrimitive> List<T> getFilteredList(Collection<OsmPrimitive> list, Class<T> type) {
64 if (list == null) return Collections.emptyList();
65 List<T> ret = new LinkedList<T>();
66 for(OsmPrimitive p: list) {
67 if (type.isInstance(p)) {
68 ret.add(type.cast(p));
69 }
70 }
71 return ret;
72 }
73
74 /**
75 * Replies the sub-collection of {@see OsmPrimitive}s of type <code>type</code> present in
76 * another collection of {@see OsmPrimitive}s. The result collection is a set.
77 *
78 * If <code>list</code> is null, replies an empty set.
79 *
80 * @param <T>
81 * @param list the original collection
82 * @param type the type to filter for
83 * @return the sub-set of OSM primitives of type <code>type</code>
84 */
85 static public <T extends OsmPrimitive> Set<T> getFilteredSet(Collection<OsmPrimitive> set, Class<T> type) {
86 if (set == null) return Collections.emptySet();
87 HashSet<T> ret = new HashSet<T>();
88 for(OsmPrimitive p: set) {
89 if (type.isInstance(p)) {
90 ret.add(type.cast(p));
91 }
92 }
93 return ret;
94 }
95
96
97 /* mappaint data */
98 public ElemStyle mappaintStyle = null;
99 public Integer mappaintVisibleCode = 0;
100 public Integer mappaintDrawnCode = 0;
101 public Collection<String> errors;
102
103 public void putError(String text, Boolean isError)
104 {
105 if(errors == null) {
106 errors = new ArrayList<String>();
107 }
108 String s = isError ? tr("Error: {0}", text) : tr("Warning: {0}", text);
109 errors.add(s);
110 }
111 public void clearErrors()
112 {
113 errors = null;
114 }
115 /* This should not be called from outside. Fixing the UI to add relevant
116 get/set functions calling this implicitely is preferred, so we can have
117 transparent cache handling in the future. */
118 protected void clearCached()
119 {
120 mappaintVisibleCode = 0;
121 mappaintDrawnCode = 0;
122 mappaintStyle = null;
123 }
124 /* end of mappaint data */
125
126 /**
127 * Unique identifier in OSM. This is used to identify objects on the server.
128 * An id of 0 means an unknown id. The object has not been uploaded yet to
129 * know what id it will get.
130 *
131 */
132 private long id = 0;
133
134 private volatile byte flags = FLAG_VISIBLE; // visible per default
135
136 /**
137 * User that last modified this primitive, as specified by the server.
138 * Never changed by JOSM.
139 */
140 private User user = null;
141
142 /**
143 * If set to true, this object is incomplete, which means only the id
144 * and type is known (type is the objects instance class)
145 */
146 public boolean incomplete = false;
147
148 /**
149 * Contains the version number as returned by the API. Needed to
150 * ensure update consistency
151 */
152 private int version = 0;
153
154 /**
155 * Creates a new primitive for the given id. If the id > 0, the primitive is marked
156 * as incomplete.
157 *
158 * @param id the id. > 0 required
159 * @param allowNegativeId Allows to set negative id. For internal use
160 * @throws IllegalArgumentException thrown if id < 0 and allowNegativeId is false
161 */
162 protected OsmPrimitive(long id, boolean allowNegativeId) throws IllegalArgumentException {
163 if (allowNegativeId) {
164 this.id = id;
165 } else {
166 if (id < 0)
167 throw new IllegalArgumentException(tr("Expected ID >= 0. Got {0}.", id));
168 else if (id == 0) {
169 this.id = generateUniqueId();
170 } else {
171 this.id = id;
172 }
173
174 }
175 this.version = 0;
176 this.incomplete = id > 0;
177 }
178
179 protected OsmPrimitive(PrimitiveData data) {
180 version = data.getVersion();
181 id = data.getId();
182 }
183
184 /* ------------------------------------------------------------------------------------ */
185 /* accessors */
186 /* ------------------------------------------------------------------------------------ */
187 /**
188 * Sets whether this primitive is disabled or not.
189 *
190 * @param disabled true, if this primitive is disabled; false, otherwise
191 */
192 public void setDisabled(boolean disabled) {
193 if (disabled) {
194 flags |= FLAG_DISABLED;
195 } else {
196 flags &= ~FLAG_DISABLED;
197 }
198
199 }
200
201 /**
202 * Replies true, if this primitive is disabled.
203 *
204 * @return true, if this primitive is disabled
205 */
206 public boolean isDisabled() {
207 return (flags & FLAG_DISABLED) != 0;
208 }
209 /**
210 * Sets whether this primitive is filtered out or not.
211 *
212 * @param filtered true, if this primitive is filtered out; false, otherwise
213 */
214 public void setFiltered(boolean filtered) {
215 if (filtered) {
216 flags |= FLAG_FILTERED;
217 } else {
218 flags &= ~FLAG_FILTERED;
219 }
220 }
221 /**
222 * Replies true, if this primitive is filtered out.
223 *
224 * @return true, if this primitive is filtered out
225 */
226 public boolean isFiltered() {
227 return (flags & FLAG_FILTERED) != 0;
228 }
229
230 /**
231 * Marks this primitive as being modified.
232 *
233 * @param modified true, if this primitive is to be modified
234 */
235 public void setModified(boolean modified) {
236 if (modified) {
237 flags |= FLAG_MODIFIED;
238 } else {
239 flags &= ~FLAG_MODIFIED;
240 }
241 }
242
243 /**
244 * Replies <code>true</code> if the object has been modified since it was loaded from
245 * the server. In this case, on next upload, this object will be updated.
246 *
247 * Deleted objects are deleted from the server. If the objects are added (id=0),
248 * the modified is ignored and the object is added to the server.
249 *
250 * @return <code>true</code> if the object has been modified since it was loaded from
251 * the server
252 */
253 public boolean isModified() {
254 return (flags & FLAG_MODIFIED) != 0;
255 }
256
257 /**
258 * Replies <code>true</code>, if the object has been deleted.
259 *
260 * @return <code>true</code>, if the object has been deleted.
261 * @see #setDeleted(boolean)
262 */
263 public boolean isDeleted() {
264 return (flags & FLAG_DELETED) != 0;
265 }
266
267 /**
268 * Replies <code>true</code>, if the object is usable.
269 *
270 * @return <code>true</code>, if the object is unusable.
271 * @see #delete(boolean)
272 */
273 public boolean isUsable() {
274 return !isDeleted() && !incomplete && !isDisabled();
275 }
276
277 /**
278 * Replies true if this primitive is either unknown to the server (i.e. its id
279 * is 0) or it is known to the server and it hasn't be deleted on the server.
280 * Replies false, if this primitive is known on the server and has been deleted
281 * on the server.
282 *
283 * @see #setVisible(boolean)
284 */
285 public boolean isVisible() {
286 return (flags & FLAG_VISIBLE) != 0;
287 }
288
289 /**
290 * Sets whether this primitive is visible, i.e. whether it is known on the server
291 * and not deleted on the server.
292 *
293 * @see #isVisible()
294 * @throws IllegalStateException thrown if visible is set to false on an primitive with
295 * id==0
296 */
297 public void setVisible(boolean visible) throws IllegalStateException{
298 if (isNew() && visible == false)
299 throw new IllegalStateException(tr("A primitive with ID = 0 can't be invisible."));
300 if (visible) {
301 flags |= FLAG_VISIBLE;
302 } else {
303 flags &= ~FLAG_VISIBLE;
304 }
305 }
306
307 /**
308 * Replies the version number as returned by the API. The version is 0 if the id is 0 or
309 * if this primitive is incomplete.
310 *
311 * @see #setVersion(int)
312 */
313 public long getVersion() {
314 return version;
315 }
316
317 /**
318 * Replies the id of this primitive.
319 *
320 * @return the id of this primitive.
321 */
322 public long getId() {
323 return id >= 0?id:0;
324 }
325
326 /**
327 *
328 * @return Osm id if primitive already exists on the server. Unique negative value if primitive is new
329 */
330 public long getUniqueId() {
331 return id;
332 }
333
334 /**
335 *
336 * @return True if primitive is new (not yet uploaded the server, id <= 0)
337 */
338 public boolean isNew() {
339 return id <= 0;
340 }
341
342 /**
343 * Sets the id and the version of this primitive if it is known to the OSM API.
344 *
345 * Since we know the id and its version it can't be incomplete anymore. incomplete
346 * is set to false.
347 *
348 * @param id the id. > 0 required
349 * @param version the version > 0 required
350 * @throws IllegalArgumentException thrown if id <= 0
351 * @throws IllegalArgumentException thrown if version <= 0
352 */
353 public void setOsmId(long id, int version) {
354 if (id <= 0)
355 throw new IllegalArgumentException(tr("ID > 0 expected. Got {0}.", id));
356 if (version <= 0)
357 throw new IllegalArgumentException(tr("Version > 0 expected. Got {0}.", version));
358 this.id = id;
359 this.version = version;
360 this.incomplete = false;
361 }
362
363 /**
364 * Clears the id and version known to the OSM API. The id and the version is set to 0.
365 * incomplete is set to false.
366 *
367 * <strong>Caution</strong>: Do not use this method on primitives which are already added to a {@see DataSet}.
368 * Ways and relations might already refer to the primitive and clearing the OSM ID
369 * result in corrupt data.
370 *
371 * Here's an example use case:
372 * <pre>
373 * // create a clone of an already existing node
374 * Node copy = new Node(otherExistingNode);
375 *
376 * // reset the clones OSM id
377 * copy.clearOsmId();
378 * </pre>
379 *
380 */
381 public void clearOsmId() {
382 this.id = generateUniqueId();
383 this.version = 0;
384 this.incomplete = false;
385 }
386
387 public void setTimestamp(Date timestamp) {
388 this.timestamp = (int)(timestamp.getTime() / 1000);
389 }
390
391 /**
392 * Time of last modification to this object. This is not set by JOSM but
393 * read from the server and delivered back to the server unmodified. It is
394 * used to check against edit conflicts.
395 *
396 */
397 public Date getTimestamp() {
398 return new Date(timestamp * 1000l);
399 }
400
401 public boolean isTimestampEmpty() {
402 return timestamp == 0;
403 }
404
405 /**
406 * If set to true, this object is highlighted. Currently this is only used to
407 * show which ways/nodes will connect
408 */
409 public volatile boolean highlighted = false;
410
411 private int timestamp;
412
413 private static Collection<String> uninteresting = null;
414 /**
415 * Contains a list of "uninteresting" keys that do not make an object
416 * "tagged".
417 * Initialized by checkTagged()
418 */
419 public static Collection<String> getUninterestingKeys() {
420 if (uninteresting == null) {
421 uninteresting = Main.pref.getCollection("tags.uninteresting",
422 Arrays.asList(new String[]{"source","note","comment","converted_by","created_by"}));
423 }
424 return uninteresting;
425 }
426
427
428 private static Collection<String> directionKeys = null;
429
430 /**
431 * Contains a list of direction-dependent keys that make an object
432 * direction dependent.
433 * Initialized by checkDirectionTagged()
434 */
435 public static Collection<String> getDirectionKeys() {
436 if(directionKeys == null) {
437 directionKeys = Main.pref.getCollection("tags.direction",
438 Arrays.asList("oneway","incline","incline_steep","aerialway"));
439 }
440 return directionKeys;
441 }
442
443 /**
444 * Implementation of the visitor scheme. Subclasses have to call the correct
445 * visitor function.
446 * @param visitor The visitor from which the visit() function must be called.
447 */
448 abstract public void visit(Visitor visitor);
449
450 /**
451 * Sets whether this primitive is deleted or not.
452 *
453 * Also marks this primitive as modified if deleted is true.
454 *
455 * @param deleted true, if this primitive is deleted; false, otherwise
456 */
457 public void setDeleted(boolean deleted) {
458 if (deleted) {
459 flags |= FLAG_DELETED;
460 } else {
461 flags &= ~FLAG_DELETED;
462 }
463 setModified(deleted);
464 }
465
466 /**
467 * Replies the user who has last touched this object. May be null.
468 *
469 * @return the user who has last touched this object. May be null.
470 */
471 public User getUser() {
472 return user;
473 }
474
475 /**
476 * Sets the user who has last touched this object.
477 *
478 * @param user the user
479 */
480 public void setUser(User user) {
481 this.user = user;
482 }
483
484 /**
485 * Equal, if the id (and class) is equal.
486 *
487 * An primitive is equal to its incomplete counter part.
488 */
489 @Override public boolean equals(Object obj) {
490 if (isNew()) return obj == this;
491 if (obj instanceof OsmPrimitive)
492 return ((OsmPrimitive)obj).id == id && obj.getClass() == getClass();
493 return false;
494 }
495
496 /**
497 * Return the id plus the class type encoded as hashcode or super's hashcode if id is 0.
498 *
499 * An primitive has the same hashcode as its incomplete counterpart.
500 */
501 @Override public final int hashCode() {
502 if (id == 0)
503 return super.hashCode();
504 final int[] ret = new int[1];
505 Visitor v = new Visitor(){
506 public void visit(Node n) { ret[0] = 0; }
507 public void visit(Way w) { ret[0] = 1; }
508 public void visit(Relation e) { ret[0] = 2; }
509 public void visit(Changeset cs) { ret[0] = 3; }
510 };
511 visit(v);
512 return id == 0 ? super.hashCode() : (int)(id<<2)+ret[0];
513 }
514
515 /*------------
516 * Keys handling
517 ------------*/
518
519 /**
520 * The key/value list for this primitive.
521 *
522 */
523 private Map<String, String> keys;
524
525 /**
526 * Replies the map of key/value pairs. Never replies null. The map can be empty, though.
527 *
528 * @return Keys of this primitive. Changes made in returned map are not mapped
529 * back to the primitive, use setKeys() to modify the keys
530 * @since 1924
531 */
532 public Map<String, String> getKeys() {
533 // TODO More effective map
534 // fix for #3218
535 if (keys == null)
536 return new HashMap<String, String>();
537 else
538 return new HashMap<String, String>(keys);
539 }
540
541 /**
542 * Sets the keys of this primitives to the key/value pairs in <code>keys</code>.
543 * If <code>keys</code> is null removes all existing key/value pairs.
544 *
545 * @param keys the key/value pairs to set. If null, removes all existing key/value pairs.
546 * @since 1924
547 */
548 public void setKeys(Map<String, String> keys) {
549 if (keys == null) {
550 this.keys = null;
551 } else {
552 this.keys = new HashMap<String, String>(keys);
553 }
554 keysChangedImpl();
555 }
556
557 /**
558 * Set the given value to the given key. If key is null, does nothing. If value is null,
559 * removes the key and behaves like {@see #remove(String)}.
560 *
561 * @param key The key, for which the value is to be set. Can be null, does nothing in this case.
562 * @param value The value for the key. If null, removes the respective key/value pair.
563 *
564 * @see #remove(String)
565 */
566 public final void put(String key, String value) {
567 if (key == null)
568 return;
569 else if (value == null) {
570 remove(key);
571 } else {
572 if (keys == null) {
573 keys = new HashMap<String, String>();
574 }
575 keys.put(key, value);
576 }
577 mappaintStyle = null;
578 keysChangedImpl();
579 }
580 /**
581 * Remove the given key from the list
582 *
583 * @param key the key to be removed. Ignored, if key is null.
584 */
585 public final void remove(String key) {
586 if (keys != null) {
587 keys.remove(key);
588 if (keys.isEmpty()) {
589 keys = null;
590 }
591 }
592 mappaintStyle = null;
593 keysChangedImpl();
594 }
595
596 /**
597 * Removes all keys from this primitive.
598 *
599 * @since 1843
600 */
601 public final void removeAll() {
602 keys = null;
603 mappaintStyle = null;
604 keysChangedImpl();
605 }
606
607 /**
608 * Replies the value for key <code>key</code>. Replies null, if <code>key</code> is null.
609 * Replies null, if there is no value for the given key.
610 *
611 * @param key the key. Can be null, replies null in this case.
612 * @return the value for key <code>key</code>.
613 */
614 public final String get(String key) {
615 if (key == null) return null;
616 return keys == null ? null : keys.get(key);
617 }
618
619 public final Collection<Entry<String, String>> entrySet() {
620 if (keys == null)
621 return Collections.emptyList();
622 return keys.entrySet();
623 }
624
625 public final Collection<String> keySet() {
626 if (keys == null)
627 return Collections.emptyList();
628 return keys.keySet();
629 }
630
631 /**
632 * Replies true, if the map of key/value pairs of this primitive is not empty.
633 *
634 * @return true, if the map of key/value pairs of this primitive is not empty; false
635 * otherwise
636 *
637 * @since 1843
638 */
639 public final boolean hasKeys() {
640 return keys != null && !keys.isEmpty();
641 }
642
643 private void keysChangedImpl() {
644 updateHasDirectionKeys();
645 updateTagged();
646 }
647
648 /**
649 * Get and write all attributes from the parameter. Does not fire any listener, so
650 * use this only in the data initializing phase
651 */
652 public void cloneFrom(OsmPrimitive osm) {
653 keys = osm.keys == null ? null : new HashMap<String, String>(osm.keys);
654 id = osm.id;
655 timestamp = osm.timestamp;
656 version = osm.version;
657 incomplete = osm.incomplete;
658 flags = osm.flags;
659 user= osm.user;
660 clearCached();
661 clearErrors();
662 }
663
664 /**
665 * Replies true if this primitive and other are equal with respect to their
666 * semantic attributes.
667 * <ol>
668 * <li>equal id</ol>
669 * <li>both are complete or both are incomplete</li>
670 * <li>both have the same tags</li>
671 * </ol>
672 * @param other
673 * @return true if this primitive and other are equal with respect to their
674 * semantic attributes.
675 */
676 public boolean hasEqualSemanticAttributes(OsmPrimitive other) {
677 if (!isNew() && id != other.id)
678 return false;
679 if (incomplete && ! other.incomplete || !incomplete && other.incomplete)
680 return false;
681 return (keys == null ? other.keys==null : keys.equals(other.keys));
682 }
683
684 /**
685 * Replies true if this primitive and other are equal with respect to their
686 * technical attributes. The attributes:
687 * <ol>
688 * <li>deleted</ol>
689 * <li>modified</ol>
690 * <li>timestamp</ol>
691 * <li>version</ol>
692 * <li>visible</ol>
693 * <li>user</ol>
694 * </ol>
695 * have to be equal
696 * @param other the other primitive
697 * @return true if this primitive and other are equal with respect to their
698 * technical attributes
699 */
700 public boolean hasEqualTechnicalAttributes(OsmPrimitive other) {
701 if (other == null) return false;
702
703 return
704 isDeleted() == other.isDeleted()
705 && isModified() == other.isModified()
706 && timestamp == other.timestamp
707 && version == other.version
708 && isVisible() == other.isVisible()
709 && (user == null ? other.user==null : user==other.user);
710 }
711
712 private void updateTagged() {
713 getUninterestingKeys();
714 if (keys != null) {
715 for (Entry<String,String> e : keys.entrySet()) {
716 if (!uninteresting.contains(e.getKey())) {
717 flags |= FLAG_TAGGED;
718 return;
719 }
720 }
721 }
722 flags &= ~FLAG_TAGGED;
723 }
724
725 /**
726 * true if this object is considered "tagged". To be "tagged", an object
727 * must have one or more "interesting" tags. "created_by" and "source"
728 * are typically considered "uninteresting" and do not make an object
729 * "tagged".
730 */
731 public boolean isTagged() {
732 return (flags & FLAG_TAGGED) != 0;
733 }
734
735 private void updateHasDirectionKeys() {
736 getDirectionKeys();
737 if (keys != null) {
738 for (Entry<String,String> e : keys.entrySet()) {
739 if (directionKeys.contains(e.getKey())) {
740 flags |= FLAG_HAS_DIRECTIONS;
741 return;
742 }
743 }
744 }
745 flags &= ~FLAG_HAS_DIRECTIONS;
746 }
747 /**
748 * true if this object has direction dependent tags (e.g. oneway)
749 */
750 public boolean hasDirectionKeys() {
751 return (flags & FLAG_HAS_DIRECTIONS) != 0;
752 }
753
754
755 /**
756 * Replies the name of this primitive. The default implementation replies the value
757 * of the tag <tt>name</tt> or null, if this tag is not present.
758 *
759 * @return the name of this primitive
760 */
761 public String getName() {
762 if (get("name") != null)
763 return get("name");
764 return null;
765 }
766
767 /**
768 * Replies the a localized name for this primitive given by the value of the tags (in this order)
769 * <ul>
770 * <li>name:lang_COUNTRY_Variant of the current locale</li>
771 * <li>name:lang_COUNTRY of the current locale</li>
772 * <li>name:lang of the current locale</li>
773 * <li>name of the current locale</li>
774 * </ul>
775 *
776 * null, if no such tag exists
777 *
778 * @return the name of this primitive
779 */
780 public String getLocalName() {
781 String key = "name:" + Locale.getDefault().toString();
782 if (get(key) != null)
783 return get(key);
784 key = "name:" + Locale.getDefault().getLanguage() + "_" + Locale.getDefault().getCountry();
785 if (get(key) != null)
786 return get(key);
787 key = "name:" + Locale.getDefault().getLanguage();
788 if (get(key) != null)
789 return get(key);
790 return getName();
791 }
792
793 /**
794 * Replies the display name of a primitive formatted by <code>formatter</code>
795 *
796 * @return the display name
797 */
798 public abstract String getDisplayName(NameFormatter formatter);
799
800 /**
801 * Loads (clone) this primitive from provided PrimitiveData
802 * @param data
803 * @param dataSet Dataset this primitive is part of. This parameter is used only
804 * temporarily. OsmPrimitive will have final field dataset in future
805 */
806 public void load(PrimitiveData data, DataSet dataSet) {
807 setKeys(data.getKeys());
808 timestamp = data.getTimestamp();
809 user = data.getUser();
810 setDeleted(data.isDeleted());
811 setModified(data.isModified());
812 setVisible(data.isVisible());
813 }
814
815 /**
816 * Save parameters of this primitive to the transport object
817 * @return
818 */
819 public abstract PrimitiveData save();
820
821 protected void saveCommonAttributes(PrimitiveData data) {
822 data.setId(id);
823 data.getKeys().clear();
824 data.getKeys().putAll(getKeys());
825 data.setTimestamp(timestamp);
826 data.setUser(user);
827 data.setDeleted(isDeleted());
828 data.setModified(isModified());
829 data.setVisible(isVisible());
830 }
831
832}
833
Note: See TracBrowser for help on using the repository browser.