]> git.argeo.org Git - lgpl/argeo-commons.git/blob - jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContent.java
b32ae302085a059bf67c67ddfcca25c173bc9f4a
[lgpl/argeo-commons.git] / jcr / org.argeo.cms.jcr / src / org / argeo / cms / jcr / acr / JcrContent.java
1 package org.argeo.cms.jcr.acr;
2
3 import java.io.Closeable;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.io.PipedInputStream;
7 import java.io.PipedOutputStream;
8 import java.util.ArrayList;
9 import java.util.Calendar;
10 import java.util.HashSet;
11 import java.util.Iterator;
12 import java.util.List;
13 import java.util.Optional;
14 import java.util.Set;
15 import java.util.concurrent.ForkJoinPool;
16
17 import javax.jcr.Node;
18 import javax.jcr.NodeIterator;
19 import javax.jcr.Property;
20 import javax.jcr.PropertyIterator;
21 import javax.jcr.PropertyType;
22 import javax.jcr.RepositoryException;
23 import javax.jcr.Value;
24 import javax.jcr.nodetype.NodeType;
25 import javax.xml.namespace.QName;
26 import javax.xml.transform.Source;
27 import javax.xml.transform.stream.StreamSource;
28
29 import org.argeo.api.acr.Content;
30 import org.argeo.api.acr.NamespaceUtils;
31 import org.argeo.api.acr.spi.ContentProvider;
32 import org.argeo.api.acr.spi.ProvidedSession;
33 import org.argeo.api.cms.CmsConstants;
34 import org.argeo.cms.acr.AbstractContent;
35 import org.argeo.cms.acr.ContentUtils;
36 import org.argeo.jcr.Jcr;
37 import org.argeo.jcr.JcrException;
38 import org.argeo.jcr.JcrUtils;
39
40 /** A JCR {@link Node} accessed as {@link Content}. */
41 public class JcrContent extends AbstractContent {
42 // private Node jcrNode;
43
44 private JcrContentProvider provider;
45 private ProvidedSession session;
46
47 private String jcrWorkspace;
48 private String jcrPath;
49
50 protected JcrContent(ProvidedSession session, JcrContentProvider provider, String jcrWorkspace, String jcrPath) {
51 this.session = session;
52 this.provider = provider;
53 this.jcrWorkspace = jcrWorkspace;
54 this.jcrPath = jcrPath;
55 }
56
57 @Override
58 public QName getName() {
59 String name = Jcr.getName(getJcrNode());
60 if (name.equals("")) {// root
61 String mountPath = provider.getMountPath();
62 name = ContentUtils.getParentPath(mountPath)[1];
63 // name = Jcr.getWorkspaceName(getJcrNode());
64 }
65 return NamespaceUtils.parsePrefixedName(provider, name);
66 }
67
68 @SuppressWarnings("unchecked")
69 @Override
70 public <A> Optional<A> get(QName key, Class<A> clss) {
71 if (isDefaultAttrTypeRequested(clss)) {
72 return Optional.of((A) get(getJcrNode(), key.toString()));
73 }
74 return Optional.of((A) Jcr.get(getJcrNode(), key.toString()));
75 }
76
77 @Override
78 public Iterator<Content> iterator() {
79 try {
80 return new JcrContentIterator(getJcrNode().getNodes());
81 } catch (RepositoryException e) {
82 throw new JcrException("Cannot list children of " + getJcrNode(), e);
83 }
84 }
85
86 @Override
87 protected Iterable<QName> keys() {
88 try {
89 Set<QName> keys = new HashSet<>();
90 for (PropertyIterator propertyIterator = getJcrNode().getProperties(); propertyIterator.hasNext();) {
91 Property property = propertyIterator.nextProperty();
92 // TODO convert standard names
93 // TODO skip technical properties
94 QName name = NamespaceUtils.parsePrefixedName(provider, property.getName());
95 keys.add(name);
96 }
97 return keys;
98 } catch (RepositoryException e) {
99 throw new JcrException("Cannot list properties of " + getJcrNode(), e);
100 }
101 }
102
103 public Node getJcrNode() {
104 try {
105 // TODO caching?
106 return provider.getJcrSession(session, jcrWorkspace).getNode(jcrPath);
107 } catch (RepositoryException e) {
108 throw new JcrException("Cannot retrieve " + jcrPath + " from workspace " + jcrWorkspace, e);
109 }
110 }
111
112 /** Cast to a standard Java object. */
113 static Object get(Node node, String property) {
114 try {
115 Property p = node.getProperty(property);
116 if (p.isMultiple()) {
117 Value[] values = p.getValues();
118 List<Object> lst = new ArrayList<>();
119 for (Value value : values) {
120 lst.add(convertSingleValue(value));
121 }
122 return lst;
123 } else {
124 Value value = node.getProperty(property).getValue();
125 return convertSingleValue(value);
126 }
127 } catch (RepositoryException e) {
128 throw new JcrException("Cannot cast value from " + property + " of node " + node, e);
129 }
130 }
131
132 static Object convertSingleValue(Value value) throws RepositoryException {
133 switch (value.getType()) {
134 case PropertyType.STRING:
135 return value.getString();
136 case PropertyType.DOUBLE:
137 return (Double) value.getDouble();
138 case PropertyType.LONG:
139 return (Long) value.getLong();
140 case PropertyType.BOOLEAN:
141 return (Boolean) value.getBoolean();
142 case PropertyType.DATE:
143 Calendar calendar = value.getDate();
144 return calendar.toInstant();
145 case PropertyType.BINARY:
146 throw new IllegalArgumentException("Binary is not supported as an attribute");
147 default:
148 return value.getString();
149 }
150 }
151
152 class JcrContentIterator implements Iterator<Content> {
153 private final NodeIterator nodeIterator;
154 // we keep track in order to be able to delete it
155 private JcrContent current = null;
156
157 protected JcrContentIterator(NodeIterator nodeIterator) {
158 this.nodeIterator = nodeIterator;
159 }
160
161 @Override
162 public boolean hasNext() {
163 return nodeIterator.hasNext();
164 }
165
166 @Override
167 public Content next() {
168 current = new JcrContent(session, provider, jcrWorkspace, Jcr.getPath(nodeIterator.nextNode()));
169 return current;
170 }
171
172 @Override
173 public void remove() {
174 if (current != null) {
175 Jcr.remove(current.getJcrNode());
176 }
177 }
178
179 }
180
181 @Override
182 public Content getParent() {
183 return new JcrContent(session, provider, jcrWorkspace, Jcr.getParentPath(getJcrNode()));
184 }
185
186 @Override
187 public Content add(QName name, QName... classes) {
188 if (classes.length > 0) {
189 QName primaryType = classes[0];
190 Node child = Jcr.addNode(getJcrNode(), name.toString(), primaryType.toString());
191 for (int i = 1; i < classes.length; i++) {
192 try {
193 child.addMixin(classes[i].toString());
194 } catch (RepositoryException e) {
195 throw new JcrException("Cannot add child to " + getJcrNode(), e);
196 }
197 }
198
199 } else {
200 Jcr.addNode(getJcrNode(), name.toString(), NodeType.NT_UNSTRUCTURED);
201 }
202 return null;
203 }
204
205 @Override
206 public void remove() {
207 Jcr.remove(getJcrNode());
208 }
209
210 @Override
211 protected void removeAttr(QName key) {
212 Property property = Jcr.getProperty(getJcrNode(), key.toString());
213 if (property != null) {
214 try {
215 property.remove();
216 } catch (RepositoryException e) {
217 throw new JcrException("Cannot remove property " + key + " from " + getJcrNode(), e);
218 }
219 }
220
221 }
222
223 /*
224 * ADAPTERS
225 */
226 public <A> A adapt(Class<A> clss) {
227 if (Source.class.isAssignableFrom(clss)) {
228 // try {
229 PipedInputStream in = new PipedInputStream();
230
231 ForkJoinPool.commonPool().execute(() -> {
232 try (PipedOutputStream out = new PipedOutputStream(in)) {
233 provider.getJcrSession(session, jcrWorkspace).exportDocumentView(jcrPath, out, true, false);
234 out.flush();
235 } catch (IOException | RepositoryException e) {
236 throw new RuntimeException("Cannot export " + jcrPath + " in workspace " + jcrWorkspace, e);
237 }
238
239 });
240 return (A) new StreamSource(in);
241 // } catch (IOException e) {
242 // throw new RuntimeException("Cannot adapt " + JcrContent.this + " to " + clss, e);
243 // }
244 } else
245
246 return super.adapt(clss);
247 }
248
249 @Override
250 public <C extends Closeable> C open(Class<C> clss) throws IOException, IllegalArgumentException {
251 if (InputStream.class.isAssignableFrom(clss)) {
252 Node node = getJcrNode();
253 if (Jcr.isNodeType(node, NodeType.NT_FILE)) {
254 try {
255 return (C) JcrUtils.getFileAsStream(node);
256 } catch (RepositoryException e) {
257 throw new JcrException("Cannot open " + jcrPath + " in workspace " + jcrWorkspace, e);
258 }
259 }
260 }
261 return super.open(clss);
262 }
263
264 @Override
265 public ProvidedSession getSession() {
266 return session;
267 }
268
269 @Override
270 public ContentProvider getProvider() {
271 return provider;
272 }
273
274 /*
275 * STATIC UTLITIES
276 */
277 public static Content nodeToContent(Node node) {
278 if (node == null)
279 return null;
280 try {
281 ProvidedSession contentSession = (ProvidedSession) node.getSession()
282 .getAttribute(ProvidedSession.class.getName());
283 if (contentSession == null)
284 throw new IllegalArgumentException(
285 "Cannot adapt " + node + " to content, because it was not loaded from a content session");
286 return contentSession.get(CmsConstants.SYS_WORKSPACE + node.getPath());
287 } catch (RepositoryException e) {
288 throw new JcrException("Cannot adapt " + node + " to a content", e);
289 }
290 }
291
292 }