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

File AbstractStringEntityReferenceResolver.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart10.png
0% of files have more coverage

Code metrics

32
73
10
1
257
148
28
0.38
7.3
10
2.8

Classes

Class Line # Actions
AbstractStringEntityReferenceResolver 42 73 0% 28 4
0.965217496.5%
 

Contributing tests

This file is covered by 665 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.internal.reference;
21   
22    import java.util.HashMap;
23    import java.util.Map;
24   
25    import javax.inject.Inject;
26   
27    import org.apache.commons.lang3.StringUtils;
28    import org.xwiki.component.phase.Initializable;
29    import org.xwiki.model.EntityType;
30    import org.xwiki.model.reference.EntityReference;
31    import org.xwiki.model.reference.EntityReferenceResolver;
32   
33    /**
34    * Generic implementation deferring default values for unspecified reference parts to extending classes. This allows for
35    * example both the Current Entity Reference Resolver and the Default Entity Reference Resolver to share the code from
36    * this class.
37    *
38    * @see AbstractEntityReferenceResolver
39    * @version $Id: 3edf4ffaf005810377ed6b87173fc9552d304826 $
40    * @since 2.2M1
41    */
 
42    public abstract class AbstractStringEntityReferenceResolver extends AbstractEntityReferenceResolver implements
43    EntityReferenceResolver<String>, Initializable
44    {
45    /**
46    * Array of character to unescape in entity names.
47    */
48    private String[] escapeMatching;
49   
50    /**
51    * The replacement array corresponding to the array in {@link #escapeMatching} array.
52    */
53    private String[] escapeMatchingReplace;
54   
55    @Inject
56    private SymbolScheme symbolScheme;
57   
58    private Map<EntityType, Map<Character, EntityType>> referenceSetup;
59   
60    /**
61    * Empty constructor, to be used by the Component Manager, which will also inject the Symbol Scheme.
62    */
 
63  1792 toggle public AbstractStringEntityReferenceResolver()
64    {
65    // Empty constructor, to be used by the Component Manager, which will also inject the Symbol Scheme
66    }
67   
68    /**
69    * Constructor to be used when using this class as a POJO and not as a component.
70    *
71    * @param symbolScheme the scheme to use for serializing the passed references (i.e. defines the separators to use
72    * between the Entity types, and the characters to escape and how to escape them)
73    */
 
74  8 toggle public AbstractStringEntityReferenceResolver(SymbolScheme symbolScheme)
75    {
76  8 this.symbolScheme = symbolScheme;
77  8 initialize();
78    }
79   
 
80  1800 toggle @Override
81    public void initialize()
82    {
83  1800 this.referenceSetup = new HashMap<>();
84   
85  1800 Map<EntityType, Map<EntityType, Character>> separators = getSymbolScheme().getSeparatorSymbols();
86  1800 for (Map.Entry<EntityType, Map<EntityType, Character>> separatorEntry : separators.entrySet()) {
87  12600 Map<Character, EntityType> characterMap = new HashMap<>();
88  12600 for (Map.Entry<EntityType, Character> characterEntry : separatorEntry.getValue().entrySet()) {
89  12600 characterMap.put(characterEntry.getValue(), characterEntry.getKey());
90    }
91  12600 this.referenceSetup.put(separatorEntry.getKey(), characterMap);
92    }
93   
94  1800 String escape = Character.toString(getSymbolScheme().getEscapeSymbol());
95  1800 this.escapeMatching = new String[] { escape + escape, escape };
96  1800 this.escapeMatchingReplace = new String[] { escape, StringUtils.EMPTY };
97    }
98   
 
99  940388 toggle @Override
100    public EntityReference resolve(String entityReferenceRepresentation, EntityType type, Object... parameters)
101    {
102  940389 Map<Character, EntityType> typeSetup = getTypeSetup(type);
103   
104    // Check if the type require anything specific
105  940352 if (typeSetup == null) {
106  0 return getEscapedReference(entityReferenceRepresentation, type, parameters);
107    }
108   
109    // Handle the case when the passed representation is null. In this case we consider it similar to passing
110    // an empty string.
111  940374 StringBuilder representation;
112  940367 if (entityReferenceRepresentation == null) {
113  2748 representation = new StringBuilder();
114    } else {
115  937627 representation = new StringBuilder(entityReferenceRepresentation);
116    }
117   
118  940371 EntityReference reference = null;
119   
120  940379 EntityType currentType = type;
121   
122  2718326 while (typeSetup != null && !typeSetup.isEmpty()) {
123    // Search all characters for a non escaped separator. If found, then consider the part after the
124    // character as the reference name and continue parsing the part before the separator.
125  1777936 EntityType parentType = null;
126   
127  1777943 int i = representation.length();
128  14554344 while (--i >= 0) {
129  13388607 char currentChar = representation.charAt(i);
130  13388555 int nextIndex = i - 1;
131  13388465 char nextChar = 0;
132  13388793 if (nextIndex >= 0) {
133  12614024 nextChar = representation.charAt(nextIndex);
134    }
135   
136  13388725 if (typeSetup.containsKey(currentChar)) {
137  612962 int numberOfEscapeChars =
138    getNumberOfCharsBefore(getSymbolScheme().getEscapeSymbol(), representation, nextIndex);
139   
140  612967 if (numberOfEscapeChars % 2 == 0) {
141  612161 parentType = typeSetup.get(currentChar);
142  612176 break;
143    } else {
144    // Unescape the character
145  795 representation.delete(nextIndex, i);
146  795 --i;
147    }
148  12775532 } else if (nextChar == getSymbolScheme().getEscapeSymbol()) {
149    // Unescape the character
150  21 representation.delete(nextIndex, i);
151  21 --i;
152    }
153    }
154   
155  1777926 reference = appendNewReference(reference, getNewReference(i, representation, currentType, parameters));
156   
157  1777948 if (parentType != null) {
158  612173 currentType = parentType;
159    } else {
160  1165770 currentType = typeSetup.values().iterator().next();
161    }
162   
163  1777945 typeSetup = getTypeSetup(currentType);
164    }
165   
166    // Handle last entity reference's name
167  940406 reference = appendNewReference(reference, getEscapedReference(representation, currentType, parameters));
168   
169  940382 return reference;
170    }
171   
172    /**
173    * The default is extracted from the default {@link SymbolScheme}, but extending classes can override it.
174    *
175    * @param type the type for which to get the setup
176    * @return the reference setup map for the requested type, consisting of &lt;parent separator, parent type&gt; pairs
177    * @since 7.4.1
178    * @since 8.0M1
179    */
 
180  2718308 toggle protected Map<Character, EntityType> getTypeSetup(EntityType type)
181    {
182  2718315 return this.referenceSetup.get(type);
183    }
184   
 
185  940377 toggle private EntityReference getEscapedReference(CharSequence representation, EntityType type, Object... parameters)
186    {
187  940375 EntityReference newReference;
188  940369 if (representation.length() > 0) {
189  133118 String name =
190    StringUtils.replaceEach(representation.toString(), this.escapeMatching, this.escapeMatchingReplace);
191  133119 if (name != null) {
192  133112 newReference = new EntityReference(name, type);
193    } else {
194  0 newReference = null;
195    }
196    } else {
197  807264 newReference = resolveDefaultReference(type, parameters);
198    }
199   
200  940394 return newReference;
201    }
202   
 
203  1777915 toggle private EntityReference getNewReference(int i, StringBuilder representation, EntityType type, Object... parameters)
204    {
205  1777918 EntityReference newReference;
206   
207    // Found a valid separator (not escaped), separate content on its left from content on its
208    // right
209  1777945 if (i == representation.length() - 1) {
210  391011 newReference = resolveDefaultReference(type, parameters);
211    } else {
212  1386936 String name = representation.substring(i + 1, representation.length());
213  1386959 newReference = new EntityReference(name, type);
214    }
215   
216  1777991 representation.delete(i < 0 ? 0 : i, representation.length());
217   
218  1777980 return newReference;
219    }
220   
 
221  2718342 toggle private EntityReference appendNewReference(EntityReference reference, EntityReference newReference)
222    {
223  2718347 if (newReference != null) {
224  2569967 if (reference != null) {
225  1629997 return reference.appendParent(newReference);
226    } else {
227  939955 return newReference;
228    }
229    }
230   
231  148395 return reference;
232    }
233   
234    /**
235    * Search how many time the provided character is found consecutively started to the provided index and before.
236    *
237    * @param c the character to be searched
238    * @param representation the string being searched
239    * @param currentPosition the current position where the search is started in backward direction
240    * @return the number of character in the found group
241    */
 
242  612944 toggle private int getNumberOfCharsBefore(char c, StringBuilder representation, int currentPosition)
243    {
244  612946 int position = currentPosition;
245   
246  613768 while (position >= 0 && representation.charAt(position) == c) {
247  809 --position;
248    }
249   
250  612961 return currentPosition - position;
251    }
252   
 
253  13391709 toggle protected SymbolScheme getSymbolScheme()
254    {
255  13391711 return this.symbolScheme;
256    }
257    }