1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.modeshape.jcr;
25
26 import java.io.IOException;
27 import java.io.OutputStream;
28 import java.text.CharacterIterator;
29 import java.text.StringCharacterIterator;
30 import java.util.Collections;
31 import java.util.HashSet;
32 import java.util.Set;
33 import javax.jcr.Node;
34 import javax.jcr.NodeIterator;
35 import javax.jcr.Property;
36 import javax.jcr.PropertyIterator;
37 import javax.jcr.PropertyType;
38 import javax.jcr.RepositoryException;
39 import javax.jcr.Value;
40 import net.jcip.annotations.NotThreadSafe;
41 import org.modeshape.common.text.TextEncoder;
42 import org.modeshape.common.text.XmlNameEncoder;
43 import org.modeshape.common.util.Base64;
44 import org.modeshape.graph.ExecutionContext;
45 import org.modeshape.graph.property.Name;
46 import org.modeshape.graph.property.ValueFactories;
47 import org.modeshape.graph.property.ValueFactory;
48 import org.xml.sax.ContentHandler;
49 import org.xml.sax.SAXException;
50 import org.xml.sax.helpers.AttributesImpl;
51
52
53
54
55
56
57
58
59 @NotThreadSafe
60 class JcrDocumentViewExporter extends AbstractJcrExporter {
61
62 private static final int ENCODE_BUFFER_SIZE = 2 << 15;
63
64 private static final TextEncoder VALUE_ENCODER = new JcrDocumentViewExporter.JcrDocumentViewPropertyEncoder();
65 private final ValueFactory<String> stringFactory;
66
67 JcrDocumentViewExporter( JcrSession session ) {
68 super(session, Collections.<String>emptyList());
69 stringFactory = session.getExecutionContext().getValueFactories().getStringFactory();
70 }
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85 @Override
86 public void exportNode( Node node,
87 ContentHandler contentHandler,
88 boolean skipBinary,
89 boolean noRecurse ) throws RepositoryException, SAXException {
90 ExecutionContext executionContext = session.getExecutionContext();
91
92 if (node instanceof JcrSharedNode) {
93
94
95 AbstractJcrNode sharedNode = ((JcrSharedNode)node).proxyNode();
96 AttributesImpl atts = new AttributesImpl();
97
98
99 addAttribute(atts, JcrLexicon.PRIMARY_TYPE, PropertyType.NAME, JcrNtLexicon.SHARE);
100
101
102 addAttribute(atts, JcrLexicon.UUID, PropertyType.STRING, node.getIdentifier());
103
104
105 Name name = sharedNode.segment().getName();
106 startElement(contentHandler, name, atts);
107 endElement(contentHandler, name);
108 return;
109 }
110
111
112 if (node.getDepth() > 0 && isXmlTextNode(node)) {
113 String xmlCharacters = getXmlCharacters(node);
114 contentHandler.characters(xmlCharacters.toCharArray(), 0, xmlCharacters.length());
115 return;
116 }
117
118
119 AttributesImpl atts = new AttributesImpl();
120 Property primaryType = ((AbstractJcrNode)node).getProperty(JcrLexicon.PRIMARY_TYPE);
121 if (primaryType != null) {
122 addAttribute(atts, primaryType, skipBinary, false);
123 }
124
125
126 PropertyIterator properties = node.getProperties();
127 while (properties.hasNext()) {
128 Property prop = properties.nextProperty();
129 addAttribute(atts, prop, skipBinary, true);
130 }
131
132
133 Name name = null;
134 ValueFactories valueFactories = executionContext.getValueFactories();
135 if (node.getDepth() == 0) {
136 name = JcrLexicon.ROOT;
137 } else {
138 name = valueFactories.getNameFactory().create(node.getName());
139 }
140
141
142 startElement(contentHandler, name, atts);
143 if (!noRecurse) {
144 NodeIterator nodes = node.getNodes();
145 while (nodes.hasNext()) {
146 exportNode(nodes.nextNode(), contentHandler, skipBinary, noRecurse);
147 }
148 }
149 endElement(contentHandler, name);
150 }
151
152 protected void addAttribute( AttributesImpl atts,
153 Name propertyName,
154 int propertyType,
155 Object value ) {
156 String valueAsString = VALUE_ENCODER.encode(stringFactory.create(value));
157 String localPropName = getPrefixedName(propertyName);
158 atts.addAttribute(propertyName.getNamespaceUri(),
159 propertyName.getLocalName(),
160 localPropName,
161 PropertyType.nameFromValue(propertyType),
162 valueAsString);
163
164 }
165
166 protected void addAttribute( AttributesImpl atts,
167 Property prop,
168 boolean skipBinary,
169 boolean skipPrimaryType ) throws RepositoryException {
170
171 Name propName = ((AbstractJcrProperty)prop).name();
172 if (skipPrimaryType && JcrLexicon.PRIMARY_TYPE.equals(propName)) return;
173
174 String localPropName = getPrefixedName(propName);
175
176 if (skipBinary && PropertyType.BINARY == prop.getType()) {
177 atts.addAttribute(propName.getNamespaceUri(),
178 propName.getLocalName(),
179 localPropName,
180 PropertyType.nameFromValue(prop.getType()),
181 "");
182 return;
183 }
184
185 Value value;
186 if (prop instanceof JcrSingleValueProperty) {
187 value = prop.getValue();
188 } else {
189
190
191 value = prop.getValues()[0];
192 }
193
194 String valueAsString;
195 if (PropertyType.BINARY == prop.getType()) {
196 StringBuffer buff = new StringBuffer(ENCODE_BUFFER_SIZE);
197 try {
198 Base64.InputStream is = new Base64.InputStream(value.getBinary().getStream(), Base64.ENCODE);
199
200 byte[] bytes = new byte[ENCODE_BUFFER_SIZE];
201 int len;
202 while (-1 != (len = is.read(bytes, 0, ENCODE_BUFFER_SIZE))) {
203 buff.append(new String(bytes, 0, len));
204 }
205 } catch (IOException ioe) {
206 throw new RepositoryException(ioe);
207 }
208 valueAsString = buff.toString();
209 } else {
210 valueAsString = VALUE_ENCODER.encode(value.getString());
211 }
212
213 atts.addAttribute(propName.getNamespaceUri(),
214 propName.getLocalName(),
215 localPropName,
216 PropertyType.nameFromValue(prop.getType()),
217 valueAsString);
218 }
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233 private boolean isXmlTextNode( Node node ) throws RepositoryException {
234
235
236 if (getPrefixedName(JcrLexicon.XMLTEXT).equals(node.getName())) {
237 if (node.getNodes().getSize() == 0) {
238
239 PropertyIterator properties = node.getProperties();
240 boolean xmlCharactersFound = false;
241
242 while (properties.hasNext()) {
243 Property property = properties.nextProperty();
244
245 if (getPrefixedName(JcrLexicon.PRIMARY_TYPE).equals(property.getName())) {
246 continue;
247 }
248
249 if (getPrefixedName(JcrLexicon.XMLCHARACTERS).equals(property.getName())) {
250 xmlCharactersFound = true;
251 continue;
252 }
253
254
255 return false;
256 }
257
258 return xmlCharactersFound;
259 }
260 }
261
262 return false;
263
264 }
265
266
267
268
269
270
271
272
273
274 private String getXmlCharacters( Node node ) throws RepositoryException {
275
276
277 assert isXmlTextNode(node);
278
279 Property xmlCharacters = node.getProperty(getPrefixedName(JcrLexicon.XMLCHARACTERS));
280
281 assert xmlCharacters != null;
282
283 if (xmlCharacters.getDefinition().isMultiple()) {
284 StringBuffer buff = new StringBuffer();
285
286 for (Value value : xmlCharacters.getValues()) {
287 buff.append(value.getString());
288 }
289
290 return buff.toString();
291 }
292
293 return xmlCharacters.getValue().getString();
294 }
295
296
297
298
299
300
301 protected static class JcrDocumentViewPropertyEncoder extends XmlNameEncoder {
302
303 private static final Set<Character> MAPPED_CHARACTERS;
304
305 static {
306 MAPPED_CHARACTERS = new HashSet<Character>();
307 MAPPED_CHARACTERS.add(' ');
308 MAPPED_CHARACTERS.add('\r');
309 MAPPED_CHARACTERS.add('\n');
310 MAPPED_CHARACTERS.add('\t');
311
312 }
313
314
315
316
317
318
319
320 @Override
321 public String encode( String text ) {
322 if (text == null) return null;
323 if (text.length() == 0) return text;
324 StringBuilder sb = new StringBuilder();
325 String hex = null;
326 CharacterIterator iter = new StringCharacterIterator(text);
327 for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
328 if (c == '_') {
329
330 char next = iter.next();
331 if (next == CharacterIterator.DONE) {
332 sb.append(c);
333 break;
334 }
335
336 if (next != 'x') {
337 sb.append(c).append(next);
338 continue;
339 }
340
341 sb.append("_x005f_");
342
343 sb.append(next);
344 } else if (!MAPPED_CHARACTERS.contains(c)) {
345
346 sb.append(c);
347 } else {
348
349 hex = Integer.toHexString(c);
350
351 if (c >= '\u0000' && c <= '\u000f') {
352 sb.append("_x000").append(hex);
353 } else if (c >= '\u0010' && c <= '\u00ff') {
354 sb.append("_x00").append(hex);
355 } else if (c >= '\u0100' && c <= '\u0fff') {
356 sb.append("_x0").append(hex);
357 } else {
358 sb.append("_x").append(hex);
359 }
360 sb.append('_');
361 }
362 }
363 return sb.toString();
364 }
365 }
366 }