1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.model.reference

File EntityReference.java

 

Coverage histogram

../../../../img/srcFileCovDistChart9.png
38% of files have more coverage

Code metrics

100
145
33
1
654
328
91
0.63
4.39
33
2.76

Classes

Class Line # Actions
EntityReference 42 145 0% 91 32
0.884892188.5%
 

Contributing tests

This file is covered by 3788 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.model.reference;
21   
22    import java.beans.Transient;
23    import java.io.Serializable;
24    import java.util.Collections;
25    import java.util.LinkedList;
26    import java.util.List;
27    import java.util.Map;
28    import java.util.TreeMap;
29   
30    import org.apache.commons.lang3.StringUtils;
31    import org.apache.commons.lang3.builder.HashCodeBuilder;
32    import org.xwiki.model.EntityType;
33    import org.xwiki.model.internal.reference.DefaultSymbolScheme;
34    import org.xwiki.model.internal.reference.LocalizedStringEntityReferenceSerializer;
35   
36    /**
37    * Represents a reference to an Entity (Document, Attachment, Space, Wiki, etc).
38    *
39    * @version $Id: badbf5f7ef34098f6d077950a1bb1ba693297299 $
40    * @since 2.2M1
41    */
 
42    public class EntityReference implements Serializable, Cloneable, Comparable<EntityReference>
43    {
44    /**
45    * Used to provide a nice and readable pretty name for the {@link #toString()} method.
46    */
47    protected static final LocalizedStringEntityReferenceSerializer TOSTRING_SERIALIZER =
48    new LocalizedStringEntityReferenceSerializer(new DefaultSymbolScheme());
49   
50    /**
51    * The version identifier for this Serializable class. Increment only if the <i>serialized</i> form of the class
52    * changes.
53    */
54    private static final long serialVersionUID = 2L;
55   
56    /**
57    * Name of this entity.
58    */
59    private String name;
60   
61    /**
62    * Parent reference of this entity.
63    */
64    private EntityReference parent;
65   
66    /**
67    * Type of this entity.
68    */
69    private EntityType type;
70   
71    /**
72    * Parameters of this entity.
73    */
74    private Map<String, Serializable> parameters;
75   
76    private transient Integer size;
77   
78    private transient List<EntityReference> referenceList;
79   
80    /**
81    * Clone an EntityReference.
82    *
83    * @param reference the reference to clone
84    * @since 3.3M2
85    */
 
86  6196665 toggle public EntityReference(EntityReference reference)
87    {
88  6196666 this(reference, reference.parent);
89    }
90   
91    /**
92    * Clone an EntityReference, but use the specified parent for its new parent.
93    *
94    * @param reference the reference to clone
95    * @param parent the new parent to use
96    * @since 3.3M2
97    */
 
98  9079529 toggle public EntityReference(EntityReference reference, EntityReference parent)
99    {
100  9079542 this(reference.name, reference.type, parent, reference.parameters);
101    }
102   
103    /**
104    * Clone an EntityReference, but replace one of the parent in the chain by an other one.
105    *
106    * @param reference the reference that is cloned
107    * @param oldReference the old parent that will be replaced
108    * @param newReference the new parent that will replace oldReference in the chain
109    * @since 3.3M2
110    */
 
111  11257316 toggle protected EntityReference(EntityReference reference, EntityReference oldReference, EntityReference newReference)
112    {
113  11257344 if (reference == null) {
114  0 throw new IllegalArgumentException("Cloned reference must not be null");
115    }
116   
117  11257363 setName(reference.name);
118  11257074 setType(reference.type);
119  11256963 setParameters(reference.parameters);
120  11257402 if (reference.parent == null) {
121  1869689 if (oldReference == null) {
122  1869692 setParent(newReference);
123    } else {
124  0 throw new IllegalArgumentException("The old reference [" + oldReference
125    + "] does not belong to the parents chain of the reference [" + reference + "]");
126    }
127  9387897 } else if (reference.parent.equals(oldReference)) {
128  7675353 setParent(newReference);
129    } else {
130  1712563 setParent(new EntityReference(reference.parent, oldReference, newReference));
131    }
132    }
133   
134    /**
135    * Create a new root EntityReference. Note: Entity reference are immutable since 3.3M2.
136    *
137    * @param name name for the newly created entity reference, could not be null.
138    * @param type type for the newly created entity reference, could not be null.
139    */
 
140  7844990 toggle public EntityReference(String name, EntityType type)
141    {
142  7845037 this(name, type, null, null);
143    }
144   
145    /**
146    * Create a new EntityReference. Note: Entity reference are immutable since 3.3M2.
147    *
148    * @param name name for the newly created entity reference, could not be null.
149    * @param type type for the newly created entity reference, could not be null.
150    * @param parent parent reference for the newly created entity reference, may be null.
151    */
 
152  7051365 toggle public EntityReference(String name, EntityType type, EntityReference parent)
153    {
154  7051358 setName(name);
155  7051375 setType(type);
156  7051228 setParent(parent);
157    }
158   
159    /**
160    * Create a new EntityReference. Note: Entity reference are immutable since 3.3M2.
161    *
162    * @param name name for the newly created entity, could not be null.
163    * @param type type for the newly created entity, could not be null.
164    * @param parent parent reference for the newly created entity reference, may be null.
165    * @param parameters parameters for this reference, may be null
166    * @since 3.3M2
167    */
 
168  16923678 toggle protected EntityReference(String name, EntityType type, EntityReference parent,
169    Map<String, Serializable> parameters)
170    {
171  16923705 setName(name);
172  16923714 setType(type);
173  16923427 setParent(parent);
174  16923253 setParameters(parameters);
175    }
176   
177    /**
178    * Entity reference are immutable since 3.3M2, so this method is now protected.
179    *
180    * @param name the name for this entity
181    * @exception IllegalArgumentException if the passed name is null or empty
182    */
 
183  35228257 toggle protected void setName(String name)
184    {
185  35228848 if (StringUtils.isEmpty(name)) {
186  2 throw new IllegalArgumentException("An Entity Reference name cannot be null or empty");
187    }
188  35226047 this.name = name;
189    }
190   
191    /**
192    * Returns the name of this entity. This method is final to ensure that name is never null and we use the private
193    * field in all other methods of this implementation (faster).
194    *
195    * @return the name of this entity.
196    */
 
197  23818706 toggle public final String getName()
198    {
199  23818291 return this.name;
200    }
201   
202    /**
203    * Entity reference are immutable since 3.3M2, so this method is now protected.
204    *
205    * @param parent the parent for this entity, may be null for a root entity.
206    */
 
207  35226463 toggle protected void setParent(EntityReference parent)
208    {
209  35225297 this.parent = parent;
210    }
211   
212    /**
213    * @return the parent of this entity, may be null for a root entity.
214    */
 
215  39584034 toggle public final EntityReference getParent()
216    {
217  39584396 return this.parent;
218    }
219   
220    /**
221    * Entity reference are immutable since 3.3M2, so this method is now protected.
222    *
223    * @param type the type for this entity
224    * @exception IllegalArgumentException if the passed type is null
225    */
 
226  35226594 toggle protected void setType(EntityType type)
227    {
228  35227049 if (type == null) {
229  1 throw new IllegalArgumentException("An Entity Reference type cannot be null");
230    }
231  35225836 this.type = type;
232    }
233   
234    /**
235    * Returns the type of this entity. This method is final to ensure that type is never null and we use the private
236    * field in all other methods of this implementation (faster).
237    *
238    * @return the type of this entity
239    */
 
240  50042794 toggle public final EntityType getType()
241    {
242  50042164 return this.type;
243    }
244   
245    /**
246    * Set multiple parameters at once.
247    *
248    * @param parameters the map of parameter to set
249    * @since 3.3M2
250    */
 
251  28177597 toggle protected void setParameters(Map<String, Serializable> parameters)
252    {
253  28177947 if (parameters != null) {
254  69431 for (Map.Entry<String, Serializable> entry : parameters.entrySet()) {
255  69467 setParameter(entry.getKey(), entry.getValue());
256    }
257    }
258    }
259   
260    /**
261    * Add or set a parameter value. Parameters should be immutable objects to prevent any weird behavior.
262    *
263    * @param name the name of the parameter
264    * @param value the value of the parameter
265    * @since 3.3M2
266    */
 
267  872331 toggle protected void setParameter(String name, Serializable value)
268    {
269  872333 if (value != null) {
270  810950 if (parameters == null) {
271  810888 parameters = new TreeMap<String, Serializable>();
272    }
273  810935 parameters.put(name, value);
274  61409 } else if (parameters != null) {
275  56833 parameters.remove(name);
276  56814 if (parameters.size() == 0) {
277  56816 parameters = null;
278    }
279    }
280    }
281   
282    /**
283    * Get the value of a parameter. Return null if the parameter is not set. This method is final so there is no way to
284    * override the map, and the private field in all other methods of this implementation (faster).
285    *
286    * @param <T> the type of the value of the requested parameter
287    * @param name the name of the parameter to get
288    * @return the value of the parameter
289    * @since 3.3M2
290    */
 
291  3984182 toggle @SuppressWarnings("unchecked")
292    protected final <T> T getParameter(String name)
293    {
294  3984217 return (this.parameters == null) ? null : (T) this.parameters.get(name);
295    }
296   
297    /**
298    * Get the parameters. This method is final so there is no way to override the map, and the private field in all
299    * other methods of this implementation (faster).
300    *
301    * @return the value of the parameter
302    * @since 5.3RC1
303    */
 
304  292 toggle final Map<String, Serializable> getParameters()
305    {
306  292 return this.parameters == null ? Collections.<String, Serializable>emptyMap() : this.parameters;
307    }
308   
309    /**
310    * @return the root parent of this entity
311    */
 
312  307 toggle @Transient
313    public EntityReference getRoot()
314    {
315  307 EntityReference reference = this;
316  645 while (reference.getParent() != null) {
317  338 reference = reference.getParent();
318    }
319  307 return reference;
320    }
321   
322    /**
323    * @return a list of references in the parents chain of this reference, ordered from root to this reference.
324    */
 
325  8577687 toggle @Transient
326    public List<EntityReference> getReversedReferenceChain()
327    {
328  8577767 if (this.referenceList == null) {
329  7139076 LinkedList<EntityReference> referenceDeque = new LinkedList<EntityReference>();
330   
331  7139092 EntityReference reference = this;
332  7139092 do {
333  10217025 referenceDeque.push(reference);
334  10216294 reference = reference.getParent();
335  10216994 } while (reference != null);
336   
337  7138822 this.referenceList = Collections.<EntityReference>unmodifiableList(referenceDeque);
338    }
339   
340  8577880 return this.referenceList;
341    }
342   
343    /**
344    * @return the number of elements in the {@link EntityReference}
345    * @since 7.2M1
346    */
 
347  1128304 toggle public int size()
348    {
349  1128294 if (this.size == null) {
350  35630 int i = 0;
351   
352  151174 for (EntityReference currentReference = this; currentReference != null; currentReference =
353    currentReference.getParent()) {
354  115561 ++i;
355    }
356   
357  35619 this.size = i;
358    }
359   
360  1128318 return this.size;
361    }
362   
363    /**
364    * Extract the last entity of the given type from this one by traversing the current entity to the root.
365    *
366    * @param type the type of the entity to be extracted
367    * @return the last entity of the given type in the current entity when traversing to the root (the current entity
368    * will be returned if it's of the passed type) or null if no entity of the passed type exist
369    */
 
370  15753532 toggle public EntityReference extractReference(EntityType type)
371    {
372  15753510 EntityReference reference = this;
373   
374  33854326 while (reference != null && reference.getType() != type) {
375  18101081 reference = reference.getParent();
376    }
377   
378  15753407 return reference;
379    }
380   
381    /**
382    * Extract the first entity of the given type from this one by traversing the current entity to the root.
383    *
384    * @param type the type of the entity to be extracted
385    * @return the first entity of the given type in the current entity when traversing to the root or null if no
386    * entity of the passed type exist
387    * @since 7.4M1
388    */
 
389  6 toggle public EntityReference extractFirstReference(EntityType type)
390    {
391  6 EntityReference reference = extractReference(type);
392   
393  8 while (reference != null && reference.getParent() != null && reference.getParent().getType().equals(type)) {
394  2 reference = reference.getParent();
395    }
396   
397  6 return reference;
398    }
399   
400    /**
401    * Return a clone of this reference, but with one of its parent replaced by another one.
402    *
403    * @param oldParent the old parent that will be replaced
404    * @param newParent the new parent that will replace oldParent in the chain. If the same as oldParent, this is
405    * returned.
406    * @return a new reference with a amended parent chain
407    * @since 3.3M2
408    */
 
409  256 toggle public EntityReference replaceParent(EntityReference oldParent, EntityReference newParent)
410    {
411  256 if (oldParent == newParent) {
412  2 return this;
413    }
414  254 return new EntityReference(this, oldParent, newParent);
415    }
416   
417    /**
418    * Return a clone of this reference with a parent appended at the root of its parents chain.
419    *
420    * @param newParent the parent that became the root parent of this reference (and its parent). If null, this is
421    * returned.
422    * @return a new reference with newParent added to its parent chain
423    */
 
424  1863408 toggle public EntityReference appendParent(EntityReference newParent)
425    {
426  1863406 if (newParent == null) {
427  0 return this;
428    }
429  1863424 return new EntityReference(this, null, newParent);
430    }
431   
432    /**
433    * Return a clone of this reference truncated to a null parent when it reach the given parent. It is very similar to
434    * replaceParent(parent, null), except that it is not overridden.
435    *
436    * @param oldParent the parent that will be replaced by a null. If null, this is returned.
437    * @return a new reference with oldParent and its descendant removed from its parent chain
438    * @since 4.0M2
439    */
 
440  7674185 toggle public EntityReference removeParent(EntityReference oldParent)
441    {
442  7674107 if (oldParent == null) {
443  106 return this;
444    }
445  7674058 return new EntityReference(this, oldParent, null);
446    }
447   
448    /**
449    * Checks if a given reference is a parent of this reference.
450    *
451    * @param expectedParent the parent reference to check
452    * @return {@code true} if the given reference is a parent of this reference, {@code false} otherwise
453    * @since 7.3RC1
454    */
 
455  17 toggle public boolean hasParent(EntityReference expectedParent)
456    {
457  17 EntityReference actualParent = getParent();
458   
459    // Handle the case when both the expectedParent and the actualParent are null.
460  17 if (actualParent == expectedParent) {
461  4 return true;
462    }
463   
464  33 while (actualParent != null && !actualParent.equals(expectedParent)) {
465  20 actualParent = actualParent.getParent();
466    }
467   
468  13 return actualParent != null;
469    }
470   
 
471  13886 toggle @Override
472    public String toString()
473    {
474  13889 StringBuilder sb = new StringBuilder(64);
475  13875 sb.append(StringUtils.capitalize(getType().getLowerCase()));
476  13888 sb.append(' ');
477  13881 sb.append(TOSTRING_SERIALIZER.serialize(this));
478  13903 return sb.toString();
479    }
480   
 
481  31250741 toggle @Override
482    public boolean equals(Object obj)
483    {
484  31253212 if (obj == this) {
485  10365578 return true;
486    }
487  20887874 if (!(obj instanceof EntityReference)) {
488  2400390 return false;
489    }
490   
491  18487477 EntityReference ref = (EntityReference) obj;
492   
493  18487506 return name.equals(ref.name) && type.equals(ref.type)
494  17733821 && (parent == null ? ref.parent == null : parent.equals(ref.parent))
495  17726303 && (parameters == null ? ref.parameters == null : parameters.equals(ref.parameters));
496    }
497   
498    /**
499    * Compared the two reference between the first reference and the last (including) <code>to</code> type of entity.
500    * <p>
501    * For example if you want to make sure some local document matches some complete document reference you can so it
502    * with localDocumentReference.equals(documentReference, EntityType.SPACE).
503    *
504    * @param otherReference the reference to compare
505    * @param to the type of the the last (including) entity reference to compare
506    * @return true if the two references are equals between the passes entity types references
507    * @since 7.2M1
508    */
 
509  4117 toggle public boolean equals(EntityReference otherReference, EntityType to)
510    {
511  4117 if (to == null) {
512  0 return equals(otherReference);
513    }
514   
515  4117 if (otherReference == this) {
516  0 return true;
517    }
518   
519  4117 if (otherReference == null) {
520  0 return false;
521    }
522   
523  4117 EntityReference currentReference1 = this;
524  4117 EntityReference currentReference2 = otherReference;
525   
526  4548 while (currentReference1 != null && currentReference1.getType().ordinal() >= to.ordinal()) {
527  4260 if (!currentReference1.equalsNonRecursive(currentReference2)) {
528  3829 return false;
529    }
530   
531  431 currentReference1 = currentReference1.getParent();
532  431 currentReference2 = currentReference2.getParent();
533    }
534   
535  288 if (currentReference2 == null || currentReference2.getType().ordinal() < to.ordinal()) {
536  288 return true;
537    }
538   
539  0 return false;
540    }
541   
542    /**
543    * Compared the two reference between the first (including) <code>from</code> type of entity and the last
544    * (including) <code>to</code> type of entity.
545    *
546    * @param otherReference the reference to compare
547    * @param from the type of the first (including) entity reference to compare
548    * @param to the type of the the last (including) entity reference to compare
549    * @return true if the two references are equals between the passes entity types references
550    * @since 7.2M1
551    */
 
552  2 toggle public boolean equals(EntityReference otherReference, EntityType from, EntityType to)
553    {
554  2 if (otherReference == this) {
555  0 return true;
556    }
557   
558  2 if (otherReference == null) {
559  0 return false;
560    }
561   
562  2 EntityReference currentReference1 = from != null ? this.extractReference(from) : this;
563  2 EntityReference currentReference2 = from != null ? otherReference.extractReference(from) : otherReference;
564   
565  2 return currentReference1.equals(currentReference2, to);
566    }
567   
568    /**
569    * Same as {@link #equals(Object)} but only the first level of the references (ignore parents).
570    *
571    * @param otherReference the reference to compare
572    * @return true if the two references are equals
573    * @since 7.2M1
574    */
 
575  4262 toggle public boolean equalsNonRecursive(EntityReference otherReference)
576    {
577  4262 if (otherReference == this) {
578  0 return true;
579    }
580   
581  4262 if (otherReference == null) {
582  0 return false;
583    }
584   
585  4262 return name.equals(otherReference.name) && type.equals(otherReference.type)
586  432 && (parameters == null ? otherReference.parameters == null : parameters.equals(otherReference.parameters));
587    }
588   
 
589  351733 toggle @Override
590    public int hashCode()
591    {
592  351732 return new HashCodeBuilder(3, 17).append(getName()).append(getType()).append(getParent())
593    .append(this.parameters).toHashCode();
594    }
595   
596    /**
597    * {@inheritDoc}
598    * <p>
599    * Note: The default implementation relies on comparing the string serialization of the 2 entities. It is the
600    * caller's responsibility to make sure that the entities are either first resolved or at least of the same type, in
601    * order for the comparison to actually make sense.
602    * </p>
603    *
604    * @see java.lang.Comparable#compareTo(java.lang.Object)
605    */
 
606  223632 toggle @Override
607    public int compareTo(EntityReference reference)
608    {
609  223643 if (reference == null) {
610  0 throw new NullPointerException("Provided reference should not be null");
611    }
612   
613  223638 if (reference == this) {
614  22650 return 0;
615    }
616   
617    // Generically compare the string serializations of the 2 references.
618  200999 int stringCompareResult = toString().compareTo(reference.toString());
619  201010 if (stringCompareResult != 0) {
620  140409 return stringCompareResult;
621    }
622   
623    // If the string serializations are the same, compare the parameters.
624  60598 return compareParameters(reference);
625    }
626   
627    /**
628    * Compare parameters of this reference and another reference.
629    *
630    * @param reference the other reference to be compare with
631    * @return 0 if parameters are equals, -1 if this reference has lower parameters, +1 otherwise
632    */
 
633  60596 toggle @SuppressWarnings("unchecked")
634    private int compareParameters(EntityReference reference)
635    {
636  60598 if (parameters != null && reference.parameters == null) {
637  1 return 1;
638    }
639   
640  60596 if (parameters != null) {
641  3 for (Map.Entry<String, Serializable> entry : parameters.entrySet()) {
642  3 Object obj = reference.parameters.get(entry.getKey());
643  3 Object myobj = entry.getValue();
644  3 if (myobj != null && myobj instanceof Comparable) {
645  3 if (obj == null) {
646  0 return 1;
647    }
648  3 return ((Comparable) myobj).compareTo(obj);
649    }
650    }
651    }
652  60591 return (reference.parameters == null) ? 0 : -1;
653    }
654    }