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