1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.jcr.util;
35
36 import info.magnolia.link.LinkException;
37 import info.magnolia.link.LinkTransformerManager;
38 import info.magnolia.link.LinkUtil;
39
40 import java.lang.reflect.Method;
41 import java.util.Collection;
42 import java.util.HashMap;
43 import java.util.HashSet;
44 import java.util.Map;
45 import java.util.Set;
46
47 import javax.jcr.Node;
48 import javax.jcr.PathNotFoundException;
49 import javax.jcr.Property;
50 import javax.jcr.PropertyIterator;
51 import javax.jcr.PropertyType;
52 import javax.jcr.RepositoryException;
53 import javax.jcr.Value;
54
55 import org.apache.commons.lang.StringUtils;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59
60
61
62
63 public class ContentMap implements Map<String, Object> {
64
65 private final static Logger log = LoggerFactory.getLogger(ContentMap.class);
66
67 private final Node content;
68
69
70
71
72 private final Map<String, Method> specialProperties = new HashMap<String, Method>();
73
74 public ContentMap(Node content) {
75 if (content == null) {
76 throw new NullPointerException("ContentMap doesn't accept null content");
77 }
78
79 this.content = content;
80
81
82
83 Class<? extends Node> clazz = content.getClass();
84 try {
85 specialProperties.put("name", clazz.getMethod("getName", (Class<?>[]) null));
86 specialProperties.put("id", clazz.getMethod("getIdentifier", (Class<?>[]) null));
87 specialProperties.put("path", clazz.getMethod("getPath", (Class<?>[]) null));
88 specialProperties.put("depth", clazz.getMethod("getDepth", (Class<?>[]) null));
89 specialProperties.put("nodeType", clazz.getMethod("getPrimaryNodeType", (Class<?>[]) null));
90 } catch (SecurityException e) {
91 log.debug(
92 "Failed to gain access to Node get***() method. Check VM security settings. "
93 + e.getLocalizedMessage(), e);
94 } catch (NoSuchMethodException e) {
95 log.debug(
96 "Failed to retrieve get***() method of Node class. Check the classpath for conflicting version of JCR classes. "
97 + e.getLocalizedMessage(), e);
98 }
99 }
100
101 @Override
102 public boolean containsKey(Object key) {
103
104 String strKey = convertKey(key);
105
106 if (!isValidKey(strKey)) {
107 return false;
108 }
109
110 if (isSpecialProperty(strKey)) {
111 return true;
112 }
113
114 try {
115 return content.hasProperty(strKey);
116 } catch (RepositoryException e) {
117
118 }
119 return false;
120 }
121
122 private String convertKey(Object key) {
123 if (key == null) {
124 return null;
125 }
126 try {
127 return (String) key;
128 } catch (ClassCastException e) {
129 log.debug("Invalid key. Expected String, but got {}.", key.getClass().getName());
130 }
131 return null;
132 }
133
134 private boolean isValidKey(String strKey) {
135 return !StringUtils.isBlank(strKey);
136 }
137
138 private boolean isSpecialProperty(String strKey) {
139 if (!strKey.startsWith("@")) {
140 return false;
141 }
142 strKey = convertDeprecatedProps(strKey);
143 return specialProperties.containsKey(StringUtils.removeStart(strKey, "@"));
144 }
145
146
147
148
149
150 private String convertDeprecatedProps(String strKey) {
151
152 if ("@UUID".equals(strKey) || "@uuid".equals(strKey)) {
153 return "@id";
154 } else if ("@handle".equals(strKey)) {
155 return "@path";
156 } else if ("@level".equals(strKey)) {
157 return "@depth";
158 }
159 return strKey;
160 }
161
162 @Override
163 public Object get(Object key) {
164 String keyStr;
165 try {
166 keyStr = (String) key;
167 } catch (ClassCastException e) {
168 throw new ClassCastException("ContentMap accepts only String as a parameters, provided object was of type "
169 + (key == null ? "null" : key.getClass().getName()));
170 }
171
172 Object prop = getNodeProperty(keyStr);
173 if (prop == null) {
174 keyStr = convertDeprecatedProps(keyStr);
175 return getSpecialProperty(keyStr);
176 }
177 return prop;
178 }
179
180 private Object getSpecialProperty(String strKey) {
181 if (isSpecialProperty(strKey)) {
182 final Method method = specialProperties.get(StringUtils.removeStart(strKey, "@"));
183 try {
184 return method.invoke(content, null);
185 } catch (Exception e) {
186 throw new RuntimeException(e);
187 }
188 }
189 return null;
190
191 }
192
193 private Object getNodeProperty(String keyStr) {
194 try {
195 if (content.hasProperty(keyStr)) {
196 Property prop = content.getProperty(keyStr);
197 int type = prop.getType();
198 if (type == PropertyType.DATE) {
199 return prop.getDate();
200 } else if (type == PropertyType.BINARY) {
201
202
203 } else if (type == PropertyType.BOOLEAN) {
204 return prop.getBoolean();
205 } else if (type == PropertyType.LONG) {
206 return prop.getLong();
207 } else if (type == PropertyType.DOUBLE) {
208 return prop.getDouble();
209 } else if (prop.isMultiple()) {
210
211 Value[] values = prop.getValues();
212
213 String[] valueStrings = new String[values.length];
214
215 for (int j = 0; j < values.length; j++) {
216 try {
217 valueStrings[j] = values[j].getString();
218 } catch (RepositoryException e) {
219 log.debug(e.getMessage());
220 }
221 }
222
223 return valueStrings;
224 } else if (LinkUtil.UUID_PATTERN.matcher(prop.getString()).find()) {
225 try {
226 return info.magnolia.link.LinkUtil.convertLinksFromUUIDPattern(prop.getString(),
227 LinkTransformerManager.getInstance().getBrowserLink(content.getPath()));
228 } catch (LinkException e) {
229 log.warn("Failed to parse links with from " + prop.getName(), e);
230 }
231 }
232
233 return prop.getString();
234 }
235
236 if (content.hasNode(keyStr)) {
237 return new ContentMap(content.getNode(keyStr));
238 }
239
240 } catch (PathNotFoundException e) {
241
242 } catch (RepositoryException e) {
243 log.warn("Failed to retrieve {} on {} with {}", new Object[] { keyStr, content, e.getMessage() });
244 }
245
246 return null;
247 }
248
249 @Override
250 public int size() {
251 try {
252 return (int) (content.getProperties().getSize() + specialProperties.size());
253 } catch (RepositoryException e) {
254
255 }
256 return specialProperties.size();
257 }
258
259 @Override
260 public Set<String> keySet() {
261 Set<String> keys = new HashSet<String>();
262 try {
263 PropertyIterator props = content.getProperties();
264 while (props.hasNext()) {
265 keys.add(props.nextProperty().getName());
266 }
267 } catch (RepositoryException e) {
268
269 }
270 for (String name : specialProperties.keySet()) {
271 keys.add(name);
272 }
273 return keys;
274 }
275
276 @Override
277 public Set<java.util.Map.Entry<String, Object>> entrySet() {
278 throw new UnsupportedOperationException("Entry collections are not supported");
279 }
280
281 @Override
282 public Collection<Object> values() {
283 throw new UnsupportedOperationException("Value collections are not supported");
284 }
285
286 @Override
287 public boolean containsValue(Object arg0) {
288 throw new UnsupportedOperationException("Value checks are not supported");
289 }
290
291 @Override
292 public boolean isEmpty() {
293
294 return false;
295 }
296
297 @Override
298 public void clear() {
299
300 }
301
302 @Override
303 public Object put(String arg0, Object arg1) {
304
305 return null;
306 }
307
308 @Override
309 public void putAll(Map<? extends String, ? extends Object> arg0) {
310
311 }
312
313 @Override
314 public Object remove(Object arg0) {
315
316 return null;
317 }
318
319 public Node getJCRNode() {
320 return content;
321 }
322 }