1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.exoplatform.services.cms.i18n.impl;
18
19 import java.io.ByteArrayInputStream;
20 import java.util.ArrayList;
21 import java.util.Calendar;
22 import java.util.GregorianCalendar;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26
27 import javax.jcr.ItemExistsException;
28 import javax.jcr.Node;
29 import javax.jcr.NodeIterator;
30 import javax.jcr.PathNotFoundException;
31 import javax.jcr.Property;
32 import javax.jcr.PropertyIterator;
33 import javax.jcr.PropertyType;
34 import javax.jcr.RepositoryException;
35 import javax.jcr.Session;
36 import javax.jcr.Value;
37 import javax.jcr.Workspace;
38 import javax.jcr.nodetype.NodeType;
39 import javax.jcr.nodetype.PropertyDefinition;
40
41 import org.exoplatform.commons.utils.ISO8601;
42 import org.exoplatform.services.cms.CmsService;
43 import org.exoplatform.services.cms.JcrInputProperty;
44 import org.exoplatform.services.cms.i18n.MultiLanguageService;
45 import org.exoplatform.services.cms.impl.Utils;
46 import org.exoplatform.services.cms.link.LinkManager;
47 import org.exoplatform.services.exceptions.SameAsDefaultLangException;
48 import org.exoplatform.services.jcr.access.PermissionType;
49 import org.exoplatform.services.jcr.core.ExtendedNode;
50 import org.exoplatform.services.jcr.impl.core.value.DateValue;
51 import org.exoplatform.services.jcr.impl.core.value.StringValue;
52 import org.exoplatform.services.log.ExoLogger;
53 import org.exoplatform.services.log.Log;
54 import org.exoplatform.services.security.IdentityConstants;
55 import org.exoplatform.services.wcm.utils.WCMCoreUtils;
56
57 public class MultiLanguageServiceImpl implements MultiLanguageService {
58
59
60
61
62 final static public String JCRCONTENT = "jcr:content";
63
64
65
66
67 final static public String JCRDATA = "jcr:data";
68
69
70
71
72 final static public String JCR_MIMETYPE = "jcr:mimeType";
73
74
75
76
77 final static public String NTUNSTRUCTURED = "nt:unstructured";
78
79
80
81
82 final static public String NTFOLDER = "nt:folder";
83
84
85
86
87 final static public String NTFILE = "nt:file";
88
89
90
91
92 final static public String JCR_LASTMODIFIED = "jcr:lastModified";
93
94
95
96
97 final static String VOTER_PROP = "exo:voter";
98
99
100
101
102 final static String VOTING_RATE_PROP = "exo:votingRate";
103
104
105
106
107 final static String VOTE_TOTAL_PROP = "exo:voteTotal";
108
109
110
111
112 final static String VOTE_TOTAL_LANG_PROP = "exo:voteTotalOfLang";
113
114
115
116
117 final static String NODE = "/node/";
118
119
120
121
122 final static String NODE_LANGUAGE = "/node/languages/";
123
124
125
126
127 final static String CONTENT_PATH = "/node/jcr:content/";
128
129
130
131
132 final static String TEMP_NODE = "temp";
133
134 private static final String MIX_REFERENCEABLE = "mix:referenceable";
135
136 private static final String MIX_COMMENTABLE ="mix:commentable";
137
138 private static final String COUNTRY_VARIANT = "_";
139
140 private static final Log LOG = ExoLogger.getLogger(MultiLanguageServiceImpl.class.getName());
141
142
143
144
145 private CmsService cmsService_ ;
146
147
148
149
150
151
152
153 public MultiLanguageServiceImpl(CmsService cmsService) throws Exception {
154 cmsService_ = cmsService ;
155 }
156
157
158
159
160
161
162
163
164
165
166 private void setPropertyValue(String propertyName,
167 Node node,
168 int requiredtype,
169 Object value,
170 boolean isMultiple) throws Exception {
171 switch (requiredtype) {
172 case PropertyType.STRING:
173 if (value == null) {
174 node.setProperty(propertyName, "");
175 } else {
176 if(isMultiple) {
177 if (value instanceof String) node.setProperty(propertyName, new String[] { value.toString()});
178 else if(value instanceof String[]) node.setProperty(propertyName, (String[]) value);
179 } else {
180 if(value instanceof StringValue) {
181 StringValue strValue = (StringValue) value ;
182 node.setProperty(propertyName, strValue.getString());
183 } else {
184 node.setProperty(propertyName, value.toString());
185 }
186 }
187 }
188 break;
189 case PropertyType.BINARY:
190 if (value == null)
191 node.setProperty(propertyName, "");
192 else if (value instanceof byte[])
193 node.setProperty(propertyName, new ByteArrayInputStream((byte[]) value));
194 else if (value instanceof String)
195 node.setProperty(propertyName, new ByteArrayInputStream((value.toString()).getBytes()));
196 else if (value instanceof String[])
197 node.setProperty(propertyName, new ByteArrayInputStream((((String[]) value)).toString()
198 .getBytes()));
199 break;
200 case PropertyType.BOOLEAN:
201 if (value == null)
202 node.setProperty(propertyName, false);
203 else if (value instanceof String)
204 node.setProperty(propertyName, new Boolean(value.toString()).booleanValue());
205 else if (value instanceof String[])
206 node.setProperty(propertyName, (String[]) value);
207 break;
208 case PropertyType.LONG:
209 if (value == null || "".equals(value))
210 node.setProperty(propertyName, 0);
211 else if (value instanceof String)
212 node.setProperty(propertyName, new Long(value.toString()).longValue());
213 else if (value instanceof String[])
214 node.setProperty(propertyName, (String[]) value);
215 break;
216 case PropertyType.DOUBLE:
217 if (value == null || "".equals(value))
218 node.setProperty(propertyName, 0);
219 else if (value instanceof String)
220 node.setProperty(propertyName, new Double(value.toString()).doubleValue());
221 else if (value instanceof String[])
222 node.setProperty(propertyName, (String[]) value);
223 break;
224 case PropertyType.DATE:
225 if (value == null) {
226 node.setProperty(propertyName, new GregorianCalendar());
227 } else {
228 if(isMultiple) {
229 Session session = node.getSession() ;
230 if (value instanceof String) {
231 Value value2add = session.getValueFactory().createValue(ISO8601.parse((String) value));
232 node.setProperty(propertyName, new Value[] {value2add});
233 } else if (value instanceof String[]) {
234 String[] values = (String[]) value;
235 Value[] convertedCalendarValues = new Value[values.length];
236 int i = 0;
237 for (String stringValue : values) {
238 Value value2add = session.getValueFactory().createValue(ISO8601.parse(stringValue));
239 convertedCalendarValues[i] = value2add;
240 i++;
241 }
242 node.setProperty(propertyName, convertedCalendarValues);
243 }
244 } else {
245 if(value instanceof String) {
246 node.setProperty(propertyName, ISO8601.parse(value.toString()));
247 } else if(value instanceof GregorianCalendar) {
248 node.setProperty(propertyName, (GregorianCalendar) value);
249 } else if(value instanceof DateValue) {
250 DateValue dateValue = (DateValue) value ;
251 node.setProperty(propertyName, dateValue.getDate());
252 }
253 }
254 }
255 break;
256 case PropertyType.REFERENCE :
257 if (value == null) {
258 node.setProperty(propertyName, "");
259 } else if (value instanceof Value) {
260 node.setProperty(propertyName, (Value)value);
261 } else if (value instanceof Value[]) {
262 node.setProperty(propertyName, (Value[]) value);
263 } else if (value instanceof String) {
264 Session session = node.getSession();
265 Node catNode = null;
266 String itemPath = value.toString();
267 if ((itemPath != null) && (itemPath.length() > 0)) {
268 if (itemPath.indexOf(":/") > -1) {
269 if (itemPath.split(":/").length > 0) itemPath = "/" + itemPath.split(":/")[1];
270 }
271 try {
272 catNode = (Node)session.getItem(itemPath);
273 } catch (PathNotFoundException e) {
274 catNode = session.getRootNode().getNode(itemPath);
275 }
276 if (catNode != null) {
277 if(!catNode.isNodeType(MIX_REFERENCEABLE)) {
278 catNode.addMixin(MIX_REFERENCEABLE);
279 catNode.save();
280 }
281 Value value2add = session.getValueFactory().createValue(catNode);
282 if(isMultiple) {
283 node.setProperty(propertyName, new Value[] {value2add});
284 } else {
285 node.setProperty(propertyName, value2add);
286 }
287 } else {
288 node.setProperty(propertyName, value.toString());
289 }
290 }
291 }
292 break ;
293 }
294 }
295
296
297
298
299
300
301
302
303
304 private void setMixin(Node node, Node newLang, boolean setValuesOnyIfCanAddMixin) throws Exception {
305 NodeType[] mixins = node.getMixinNodeTypes() ;
306 for(NodeType mixin:mixins) {
307 if(canCopy(mixin)) {
308 boolean mixinAdded = false;
309 if(newLang.canAddMixin(mixin.getName())) {
310 newLang.addMixin(mixin.getName()) ;
311 mixinAdded = true;
312 }
313 if (!setValuesOnyIfCanAddMixin || mixinAdded) {
314 for(PropertyDefinition def: mixin.getPropertyDefinitions()) {
315 if(!def.isProtected()) {
316 String propName = def.getName() ;
317 if(def.isMandatory() && !def.isAutoCreated()) {
318 if(def.isMultiple()) {
319 newLang.setProperty(propName,node.getProperty(propName).getValues()) ;
320 } else {
321 newLang.setProperty(propName,node.getProperty(propName).getValue()) ;
322 }
323 }
324 }
325 }
326 }
327 }
328 }
329 }
330
331
332
333
334
335
336
337
338 private void setMixin(Node node, Node newLang) throws Exception {
339 setMixin(node, newLang, true);
340 }
341
342
343
344
345
346
347
348
349
350
351
352
353 private Node addNewFileNode(String fileName,
354 Node newLanguageNode,
355 Value value,
356 Object lastModified,
357 String mimeType,
358 String repositoryName) throws Exception {
359 Map<String,JcrInputProperty> inputProperties = new HashMap<String,JcrInputProperty>() ;
360 JcrInputProperty nodeInput = new JcrInputProperty() ;
361 nodeInput.setJcrPath("/node") ;
362 nodeInput.setValue(fileName) ;
363 nodeInput.setMixintype("mix:i18n,mix:votable,mix:commentable") ;
364 nodeInput.setType(JcrInputProperty.NODE) ;
365 inputProperties.put("/node",nodeInput) ;
366
367 JcrInputProperty jcrContent = new JcrInputProperty() ;
368 jcrContent.setJcrPath("/node/jcr:content") ;
369 jcrContent.setValue("") ;
370 jcrContent.setMixintype("dc:elementSet") ;
371 jcrContent.setNodetype("nt:resource") ;
372 jcrContent.setType(JcrInputProperty.NODE) ;
373 inputProperties.put("/node/jcr:content",jcrContent) ;
374
375 JcrInputProperty jcrData = new JcrInputProperty() ;
376 jcrData.setJcrPath("/node/jcr:content/jcr:data") ;
377 jcrData.setValue(value.getStream()) ;
378 inputProperties.put("/node/jcr:content/jcr:data",jcrData) ;
379
380 JcrInputProperty jcrMimeType = new JcrInputProperty() ;
381 jcrMimeType.setJcrPath("/node/jcr:content/jcr:mimeType") ;
382 jcrMimeType.setValue(mimeType) ;
383 inputProperties.put("/node/jcr:content/jcr:mimeType",jcrMimeType) ;
384
385 JcrInputProperty jcrLastModified = new JcrInputProperty() ;
386 jcrLastModified.setJcrPath("/node/jcr:content/jcr:lastModified") ;
387 jcrLastModified.setValue(lastModified) ;
388 inputProperties.put("/node/jcr:content/jcr:lastModified",jcrLastModified) ;
389
390 JcrInputProperty jcrEncoding = new JcrInputProperty() ;
391 jcrEncoding.setJcrPath("/node/jcr:content/jcr:encoding") ;
392 jcrEncoding.setValue("UTF-8") ;
393 inputProperties.put("/node/jcr:content/jcr:encoding",jcrEncoding) ;
394 cmsService_.storeNode(NTFILE, newLanguageNode, inputProperties, true) ;
395 return newLanguageNode.getNode(fileName) ;
396 }
397
398
399
400
401 private Node getFileLangNode(Node languageNode) throws Exception {
402 if(languageNode.getNodes().getSize() > 0) {
403 NodeIterator nodeIter = languageNode.getNodes() ;
404 while(nodeIter.hasNext()) {
405 Node ntFile = nodeIter.nextNode() ;
406 if(ntFile.isNodeType(NTFILE)) {
407 return ntFile ;
408 }
409 }
410 return languageNode ;
411 }
412 return languageNode ;
413 }
414
415
416
417
418 public void addLanguage(Node node, Map inputs, String language, boolean isDefault) throws Exception {
419 Node newLanguageNode = null ;
420 Node languagesNode = null ;
421 String defaultLanguage = getDefault(node) ;
422 String primaryNodeTypeName = node.getPrimaryNodeType().getName();
423 if(node.hasNode(LANGUAGES)) languagesNode = node.getNode(LANGUAGES) ;
424 else {
425 languagesNode = node.addNode(LANGUAGES, NTUNSTRUCTURED) ;
426 if(languagesNode.canAddMixin("exo:hiddenable"))
427 languagesNode.addMixin("exo:hiddenable");
428 }
429 if(!defaultLanguage.equals(language)){
430 if(isDefault) {
431 if(languagesNode.hasNode(defaultLanguage)) {
432 newLanguageNode = languagesNode.getNode(defaultLanguage) ;
433 } else {
434 newLanguageNode = languagesNode.addNode(defaultLanguage, primaryNodeTypeName) ;
435 setMixin(node, newLanguageNode, false);
436 }
437 } else {
438 if(languagesNode.hasNode(language)) {
439 newLanguageNode = languagesNode.getNode(language) ;
440 } else {
441 newLanguageNode = languagesNode.addNode(language, primaryNodeTypeName) ;
442 setMixin(node, newLanguageNode, false);
443 newLanguageNode.setProperty(EXO_LANGUAGE, language) ;
444 }
445 }
446 }
447
448 setPropertyLanguage(node, newLanguageNode, inputs, isDefault, defaultLanguage, language);
449 if(isDefault && languagesNode.hasNode(language)) languagesNode.getNode(language).remove() ;
450 }
451
452
453
454
455 public void addLinkedLanguage(Node node, Node translationNode, boolean forceReplace) throws Exception {
456 Node languagesNode;
457 if (node.hasNode(LANGUAGES))
458 languagesNode = node.getNode(LANGUAGES);
459 else {
460 languagesNode = node.addNode(LANGUAGES, "nt:unstructured");
461 if (languagesNode.canAddMixin("exo:hiddenable"))
462 languagesNode.addMixin("exo:hiddenable");
463 }
464 if (!translationNode.isNodeType("mix:i18n")) {
465 translationNode.addMixin("mix:i18n");
466 translationNode.save();
467 }
468 if (!node.isNodeType("mix:i18n")) {
469 node.addMixin("mix:i18n");
470 node.save();
471 }
472 String lang = translationNode.getProperty("exo:language").getString();
473 if (languagesNode.hasNode(lang)) {
474 if (forceReplace) {
475 languagesNode.getNode(lang).remove();
476 languagesNode.save();
477 } else {
478 throw new ItemExistsException();
479 }
480 } else if (getDefault(node).equals(lang)) {
481 throw new SameAsDefaultLangException();
482 }
483 LinkManager linkManager = WCMCoreUtils.getService(LinkManager.class);
484 Node linkNode = linkManager.createLink(languagesNode, "exo:symlink", translationNode, lang);
485 ((ExtendedNode)linkNode).setPermission(IdentityConstants.ANY, new String[]{PermissionType.READ});
486 linkNode.getSession().save();
487 }
488
489
490
491
492 public void addLinkedLanguage(Node node, Node translationNode) throws Exception {
493 addLinkedLanguage(node, translationNode, false);
494 }
495
496
497
498
499 public void addSynchronizedLinkedLanguage(Node selectedNode, Node newTranslationNode) throws Exception {
500 if (newTranslationNode != null && newTranslationNode.isNodeType(Utils.EXO_SYMLINK)) {
501 newTranslationNode = WCMCoreUtils.getService(LinkManager.class).getTarget(newTranslationNode);
502 }
503
504 if (!newTranslationNode.isNodeType("mix:i18n")) {
505 newTranslationNode.addMixin("mix:i18n");
506 newTranslationNode.save();
507 }
508
509 String newLang = newTranslationNode.getProperty("exo:language").getString();
510
511
512
513 if (getLanguage(selectedNode, newLang) == null) {
514
515
516
517 List<Node> realTranslationNodes = getRealTranslationNodes(selectedNode);
518 for (Node node : realTranslationNodes) {
519 try {
520 addLinkedLanguage(node, newTranslationNode);
521 }
522 catch(ItemExistsException ex) {
523 if (LOG.isInfoEnabled()) {
524 LOG.info(String.format("Language %s already existed for %s", newLang, node.getPath()));
525 }
526 }
527
528
529 try {
530 addLinkedLanguage(newTranslationNode, node);
531 }
532 catch(ItemExistsException ex) {
533 if (LOG.isInfoEnabled()) {
534 LOG.info(String.format("Language %s already existed for %s",
535 node.getProperty("exo:language").getString(),
536 newTranslationNode.getPath()));
537 }
538 }
539 }
540
541 try {
542 addLinkedLanguage(newTranslationNode, selectedNode);
543 }
544 catch(ItemExistsException ex) {
545 if (LOG.isInfoEnabled()) {
546 LOG.info(String.format("Language %s already existed for %s",
547 selectedNode.getProperty("exo:language").getString(),
548 newTranslationNode.getPath()));
549 }
550 }
551
552
553 addLinkedLanguage(selectedNode, newTranslationNode);
554 } else {
555 throw new ItemExistsException();
556 }
557 }
558
559
560
561
562 public void addLanguage(Node node, Map inputs, String language, boolean isDefault, String nodeType) throws Exception {
563 Node newLanguageNode = null ;
564 Node languagesNode = null ;
565 String primaryNodeTypeName = node.getPrimaryNodeType().getName();
566 String defaultLanguage = getDefault(node) ;
567 Workspace ws = node.getSession().getWorkspace() ;
568 if(node.hasNode(LANGUAGES)) languagesNode = node.getNode(LANGUAGES) ;
569 else {
570 languagesNode = node.addNode(LANGUAGES, NTUNSTRUCTURED) ;
571 if(languagesNode.canAddMixin("exo:hiddenable"))
572 languagesNode.addMixin("exo:hiddenable");
573 }
574 if(!defaultLanguage.equals(language)){
575 if(isDefault) {
576 if(languagesNode.hasNode(defaultLanguage)) {
577 newLanguageNode = languagesNode.getNode(defaultLanguage) ;
578 } else {
579 newLanguageNode = languagesNode.addNode(defaultLanguage, primaryNodeTypeName) ;
580 setMixin(node, newLanguageNode, false);
581 }
582 } else {
583 if(languagesNode.hasNode(language)) {
584 newLanguageNode = languagesNode.getNode(language) ;
585 } else {
586 newLanguageNode = languagesNode.addNode(language, primaryNodeTypeName) ;
587 setMixin(node, newLanguageNode, false);
588 newLanguageNode.setProperty(EXO_LANGUAGE, language) ;
589 }
590 }
591 Node jcrContent = node.getNode(nodeType) ;
592 if ("jcr:content".equals(nodeType)) {
593 Node jcrContentNode = newLanguageNode.addNode("jcr:content", "nt:resource");
594 jcrContentNode.setProperty("jcr:lastModified", new GregorianCalendar());
595 jcrContentNode.setProperty("jcr:mimeType", "text/plain");
596 jcrContentNode.setProperty("jcr:data", "");
597 }
598 node.save() ;
599 if(!newLanguageNode.hasNode(nodeType)) {
600 ws.copy(jcrContent.getPath(), newLanguageNode.getPath() + "/" + jcrContent.getName()) ;
601 }
602 Node newContentNode = newLanguageNode.getNode(nodeType) ;
603 PropertyIterator props = newContentNode.getProperties() ;
604 while(props.hasNext()) {
605 Property prop = props.nextProperty() ;
606 if(inputs.containsKey(NODE + nodeType + "/" + prop.getName())) {
607 JcrInputProperty inputVariable = (JcrInputProperty) inputs.get(NODE + nodeType + "/" + prop.getName()) ;
608 boolean isMultiple = prop.getDefinition().isMultiple() ;
609 setPropertyValue(prop.getName(), newContentNode, prop.getType(), inputVariable.getValue(), isMultiple) ;
610 }
611 }
612 if(isDefault) {
613 Node tempNode = node.addNode(TEMP_NODE, "nt:unstructured") ;
614 node.getSession().move(node.getNode(nodeType).getPath(), tempNode.getPath() + "/" + nodeType) ;
615 node.getSession().move(newLanguageNode.getNode(nodeType).getPath(), node.getPath() + "/" + nodeType) ;
616 node.getSession().move(tempNode.getNode(nodeType).getPath(),
617 languagesNode.getPath() + "/" + defaultLanguage + "/" + nodeType);
618 tempNode.remove() ;
619 }
620 } else {
621 JcrInputProperty inputVariable = (JcrInputProperty) inputs.get(NODE + nodeType + "/" + JCRDATA) ;
622 setPropertyValue(JCRDATA, node.getNode(nodeType), inputVariable.getType(), inputVariable.getValue(), false) ;
623 }
624 setPropertyLanguage(node, newLanguageNode, inputs, isDefault, defaultLanguage, language);
625 if(isDefault && languagesNode.hasNode(language)) languagesNode.getNode(language).remove() ;
626 }
627
628
629
630
631 public void addFileLanguage(Node node,
632 String fileName,
633 Value value,
634 String mimeType,
635 String language,
636 String repositoryName,
637 boolean isDefault) throws Exception {
638 Node newLanguageNode = null ;
639 Node languagesNode = null ;
640 String defaultLanguage = getDefault(node) ;
641 Node ntFileLangNode = null ;
642 Node oldJcrContent = node.getNode(JCRCONTENT) ;
643 String olfFileName = node.getName() ;
644 Value oldValue = oldJcrContent.getProperty(JCRDATA).getValue() ;
645 String oldMimeType = oldJcrContent.getProperty(JCR_MIMETYPE).getString() ;
646 Calendar oldLastModified = new GregorianCalendar();
647 oldLastModified.setTime(oldJcrContent.getProperty(JCR_LASTMODIFIED).getDate().getTime());
648 try {
649 languagesNode = node.getNode(LANGUAGES) ;
650 } catch(PathNotFoundException pe) {
651 languagesNode = node.addNode(LANGUAGES, NTUNSTRUCTURED) ;
652 if(languagesNode.canAddMixin("exo:hiddenable"))
653 languagesNode.addMixin("exo:hiddenable");
654 }
655 if(!defaultLanguage.equals(language)){
656 if(isDefault) {
657 try {
658 newLanguageNode = languagesNode.getNode(defaultLanguage) ;
659 } catch(PathNotFoundException pe) {
660 newLanguageNode = languagesNode.addNode(defaultLanguage) ;
661 if (newLanguageNode.canAddMixin(MIX_COMMENTABLE)) {
662 newLanguageNode.addMixin(MIX_COMMENTABLE);
663 }
664 }
665 oldJcrContent.setProperty(JCR_MIMETYPE, mimeType) ;
666 oldJcrContent.setProperty(JCRDATA, value) ;
667 oldJcrContent.setProperty(JCR_LASTMODIFIED, new GregorianCalendar()) ;
668 oldJcrContent.save();
669 } else {
670 try {
671 newLanguageNode = languagesNode.getNode(language) ;
672 } catch(PathNotFoundException pe) {
673 newLanguageNode = languagesNode.addNode(language) ;
674 if (newLanguageNode.canAddMixin(MIX_COMMENTABLE)) {
675 newLanguageNode.addMixin(MIX_COMMENTABLE);
676 }
677 if(languagesNode.canAddMixin("exo:hiddenable"))
678 languagesNode.addMixin("exo:hiddenable");
679 }
680 }
681 try {
682 ntFileLangNode = newLanguageNode.getNode(fileName) ;
683 } catch(PathNotFoundException pe) {
684 node.save();
685 if(isDefault) {
686 ntFileLangNode = addNewFileNode(olfFileName,
687 newLanguageNode,
688 oldValue,
689 oldLastModified,
690 oldMimeType,
691 repositoryName);
692 } else {
693 ntFileLangNode = addNewFileNode(fileName, newLanguageNode, value,
694 new GregorianCalendar(), mimeType, repositoryName) ;
695
696 }
697 }
698 Node newJcrContent = ntFileLangNode.getNode(JCRCONTENT) ;
699 newJcrContent.setProperty(JCR_LASTMODIFIED, new GregorianCalendar());
700 setMixin(node, ntFileLangNode) ;
701 } else {
702 node.getNode(JCRCONTENT).setProperty(JCRDATA, value) ;
703 }
704 if(!defaultLanguage.equals(language) && isDefault){
705 Node selectedFileLangeNode = null ;
706 if(languagesNode.hasNode(language)) {
707 Node selectedLangNode = languagesNode.getNode(language) ;
708 selectedFileLangeNode = selectedLangNode.getNode(node.getName()) ;
709 }
710 setVoteProperty(ntFileLangNode, node, selectedFileLangeNode) ;
711 setCommentNode(node, ntFileLangNode, selectedFileLangeNode) ;
712 }
713 if(isDefault) node.setProperty(EXO_LANGUAGE, language) ;
714 node.getSession().save() ;
715 }
716
717
718
719
720 public void addFileLanguage(Node node, String language, Map mappings, boolean isDefault) throws Exception {
721 Node newLanguageNode = null ;
722 Node languagesNode = null ;
723 String primaryNodeTypeName = node.getPrimaryNodeType().getName();
724 String defaultLanguage = getDefault(node) ;
725 if(node.hasNode(LANGUAGES)) languagesNode = node.getNode(LANGUAGES) ;
726 else {
727 languagesNode = node.addNode(LANGUAGES, NTUNSTRUCTURED) ;
728 if(languagesNode.canAddMixin("exo:hiddenable"))
729 languagesNode.addMixin("exo:hiddenable");
730 }
731 if(!defaultLanguage.equals(language)){
732 if(isDefault) {
733 if(languagesNode.hasNode(defaultLanguage)) newLanguageNode = languagesNode.getNode(defaultLanguage) ;
734 else newLanguageNode = languagesNode.addNode(defaultLanguage, primaryNodeTypeName) ;
735 } else {
736 if(languagesNode.hasNode(language)) newLanguageNode = languagesNode.getNode(language) ;
737 else newLanguageNode = languagesNode.addNode(language, primaryNodeTypeName) ;
738 }
739 Node jcrContent = node.getNode(JCRCONTENT) ;
740 if(!newLanguageNode.hasNode(JCRCONTENT)) {
741 Node newJcrContent = newLanguageNode.addNode(JCRCONTENT, "nt:resource");
742 newJcrContent.setProperty(JCR_MIMETYPE, jcrContent.getProperty(JCR_MIMETYPE).getValue());
743 newJcrContent.setProperty(JCRDATA, jcrContent.getProperty(JCRDATA).getValue()) ;
744 newJcrContent.setProperty(JCR_LASTMODIFIED, new GregorianCalendar());
745 }
746 node.save() ;
747 Node newContentNode = newLanguageNode.getNode(JCRCONTENT) ;
748 PropertyIterator props = newContentNode.getProperties() ;
749 while (props.hasNext()) {
750 Property prop = props.nextProperty() ;
751 if(mappings.containsKey(CONTENT_PATH + prop.getName())) {
752 JcrInputProperty inputVariable = (JcrInputProperty) mappings.get(CONTENT_PATH + prop.getName()) ;
753 boolean isMultiple = prop.getDefinition().isMultiple() ;
754 setPropertyValue(prop.getName(), newContentNode, prop.getType(), inputVariable.getValue(), isMultiple) ;
755 }
756 }
757 if (isDefault) {
758 Node tempNode = node.addNode(TEMP_NODE, "nt:unstructured");
759 node.getSession().move(node.getNode(JCRCONTENT).getPath(),
760 tempNode.getPath() + "/" + JCRCONTENT);
761 node.getSession().move(newLanguageNode.getNode(JCRCONTENT).getPath(),
762 node.getPath() + "/" + JCRCONTENT);
763 node.getSession().move(tempNode.getNode(JCRCONTENT).getPath(),
764 languagesNode.getPath() + "/" + defaultLanguage + "/" + JCRCONTENT);
765 tempNode.remove();
766 }
767
768 setMixin(node, newLanguageNode) ;
769 } else {
770 JcrInputProperty inputVariable = (JcrInputProperty) mappings.get(CONTENT_PATH + JCRDATA) ;
771 setPropertyValue(JCRDATA, node.getNode(JCRCONTENT), inputVariable.getType(), inputVariable.getValue(), false) ;
772 }
773 setPropertyLanguage(node, newLanguageNode, mappings, isDefault, defaultLanguage, language);
774 }
775
776
777
778
779 public String getDefault(Node node) throws Exception {
780 if(node.hasProperty(EXO_LANGUAGE)) return node.getProperty(EXO_LANGUAGE).getString() ;
781 return null ;
782 }
783
784
785
786
787 public List<String> getSupportedLanguages(Node node) throws Exception {
788 List<String> languages = new ArrayList<String>();
789 String defaultLang = getDefault(node) ;
790 if(defaultLang != null) languages.add(defaultLang) ;
791 if(node.hasNode(LANGUAGES)){
792 Node languageNode = node.getNode(LANGUAGES) ;
793 NodeIterator iter = languageNode.getNodes() ;
794 while(iter.hasNext()) {
795 languages.add(iter.nextNode().getName());
796 }
797 }
798 return languages;
799 }
800
801
802
803
804
805
806
807 private List<Node> getRealTranslationNodes(Node node) throws Exception {
808 LinkManager linkManager = WCMCoreUtils.getService(LinkManager.class);
809 List<Node> translationNodes = new ArrayList<Node>();
810 if(node.hasNode(LANGUAGES)){
811 Node languageNode = node.getNode(LANGUAGES) ;
812 NodeIterator iter = languageNode.getNodes() ;
813 while(iter.hasNext()) {
814 Node currNode = iter.nextNode();
815 if (currNode.isNodeType("exo:symlink")) {
816 translationNodes.add(linkManager.getTarget(currNode));
817 }
818 }
819 }
820 return translationNodes;
821 }
822
823
824
825
826
827
828
829
830
831 private void setVoteProperty(Node newLang, Node node, Node selectedLangNode) throws Exception {
832 if(hasMixin(newLang, "mix:votable")) {
833 newLang.setProperty(VOTE_TOTAL_PROP, getVoteTotal(node)) ;
834 newLang.setProperty(VOTE_TOTAL_LANG_PROP, node.getProperty(VOTE_TOTAL_LANG_PROP).getLong()) ;
835 newLang.setProperty(VOTING_RATE_PROP, node.getProperty(VOTING_RATE_PROP).getLong()) ;
836 if(node.hasProperty(VOTER_PROP)) {
837 newLang.setProperty(VOTER_PROP, node.getProperty(VOTER_PROP).getValues()) ;
838 }
839 if(selectedLangNode != null) {
840 node.setProperty(VOTE_TOTAL_PROP, getVoteTotal(node)) ;
841 if(selectedLangNode.hasProperty(VOTE_TOTAL_LANG_PROP)) {
842 node.setProperty(VOTE_TOTAL_LANG_PROP, selectedLangNode.getProperty(VOTE_TOTAL_LANG_PROP).getLong()) ;
843 } else {
844 node.setProperty(VOTE_TOTAL_LANG_PROP, 0) ;
845 }
846 if(selectedLangNode.hasProperty(VOTING_RATE_PROP)) {
847 node.setProperty(VOTING_RATE_PROP, selectedLangNode.getProperty(VOTING_RATE_PROP).getLong()) ;
848 } else {
849 node.setProperty(VOTING_RATE_PROP, 0) ;
850 }
851 if(selectedLangNode.hasProperty(VOTER_PROP)) {
852 node.setProperty(VOTER_PROP, selectedLangNode.getProperty(VOTER_PROP).getValues()) ;
853 }
854 } else {
855 node.setProperty(VOTE_TOTAL_PROP, getVoteTotal(node)) ;
856 node.setProperty(VOTE_TOTAL_LANG_PROP, 0) ;
857 node.setProperty(VOTING_RATE_PROP, 0) ;
858 }
859 }
860 }
861
862
863
864
865
866
867
868
869
870 private void setCommentNode(Node node, Node newLang, Node selectedLangNode) throws Exception {
871 if(node.hasNode(COMMENTS)) {
872 node.getSession().move(node.getPath() + "/" + COMMENTS, newLang.getPath() + "/" + COMMENTS) ;
873 }
874 if(selectedLangNode != null && selectedLangNode.hasNode(COMMENTS)) {
875 node.getSession().move(selectedLangNode.getPath() + "/" + COMMENTS, node.getPath() + "/" + COMMENTS) ;
876 }
877 }
878
879
880
881
882
883
884
885 private long getVoteTotal(Node node) throws Exception {
886 long voteTotal = 0;
887 if(!node.hasNode(LANGUAGES) && node.hasProperty(VOTE_TOTAL_PROP)) {
888 return node.getProperty(VOTE_TOTAL_LANG_PROP).getLong() ;
889 }
890 Node multiLanguages = node.getNode(LANGUAGES) ;
891 if (node.hasProperty(VOTE_TOTAL_LANG_PROP))
892 voteTotal = node.getProperty(VOTE_TOTAL_LANG_PROP).getLong();
893 NodeIterator nodeIter = multiLanguages.getNodes() ;
894 String defaultLang = getDefault(node) ;
895 while(nodeIter.hasNext()) {
896 Node languageNode = nodeIter.nextNode() ;
897 if(node.isNodeType(NTFILE)) {
898 Node jcrContentNode = node.getNode(JCRCONTENT);
899 if(!jcrContentNode.getProperty(JCR_MIMETYPE).getString().startsWith("text")) {
900 languageNode = getFileLangNode(languageNode) ;
901 }
902 }
903 if(!languageNode.getName().equals(defaultLang) && languageNode.hasProperty(VOTE_TOTAL_LANG_PROP)) {
904 voteTotal = voteTotal + languageNode.getProperty(VOTE_TOTAL_LANG_PROP).getLong() ;
905 }
906 }
907 return voteTotal ;
908 }
909
910
911
912
913
914
915
916
917
918 private boolean hasMixin(Node node, String nodeTypeName) throws Exception {
919 NodeType[] mixinTypes = node.getMixinNodeTypes() ;
920 for(NodeType nodeType : mixinTypes) {
921 if(nodeType.getName().equals(nodeTypeName)) return true ;
922 }
923 return false ;
924 }
925
926
927
928
929
930
931 private boolean canCopy(NodeType mixin) {
932 final String name = mixin.getName();
933
934
935 return !name.equals("exo:actionable") && !name.equals("mix:versionable");
936 }
937
938 private void setPropertyLanguage(Node node,
939 Node newLanguageNode,
940 Map mappings,
941 boolean isDefault,
942 String defaultLanguage,
943 String language) throws Exception {
944 PropertyDefinition[] properties = node.getPrimaryNodeType().getPropertyDefinitions() ;
945 for(PropertyDefinition pro : properties){
946 if(!pro.isProtected()) {
947 String propertyName = pro.getName() ;
948 JcrInputProperty property = (JcrInputProperty)mappings.get(NODE + propertyName) ;
949 if(defaultLanguage.equals(language) && property != null) {
950 setPropertyValue(propertyName, node, pro.getRequiredType(), property.getValue(), pro.isMultiple()) ;
951 } else {
952 if(isDefault) {
953 if(node.hasProperty(propertyName)) {
954 Object value = null ;
955 int requiredType = node.getProperty(propertyName).getDefinition().getRequiredType() ;
956 boolean isMultiple = node.getProperty(propertyName).getDefinition().isMultiple() ;
957 if(isMultiple) value = node.getProperty(propertyName).getValues() ;
958 else value = node.getProperty(propertyName).getValue() ;
959 setPropertyValue(propertyName, newLanguageNode, requiredType, value, isMultiple) ;
960 }
961 if(property != null) {
962 setPropertyValue(propertyName, node, pro.getRequiredType(), property.getValue(), pro.isMultiple()) ;
963 }
964 } else {
965 if (property != null) {
966 setPropertyValue(propertyName,
967 newLanguageNode,
968 pro.getRequiredType(),
969 property.getValue(),
970 pro.isMultiple());
971 }
972 }
973 }
974 }
975 }
976 if (!defaultLanguage.equals(language) && isDefault) {
977 Node selectedLangNode = null;
978 Node languagesNode = node.getNode(LANGUAGES);
979 if (languagesNode.hasNode(language))
980 selectedLangNode = languagesNode.getNode(language);
981 setVoteProperty(newLanguageNode, node, selectedLangNode);
982 setCommentNode(node, newLanguageNode, selectedLangNode);
983 }
984 if(isDefault) node.setProperty(EXO_LANGUAGE, language) ;
985 node.save();
986 node.getSession().save();
987 }
988
989
990
991 public void setDefault(Node node, String language, String repositoryName) throws Exception {
992 String defaultLanguage = getDefault(node) ;
993 String nodeTypeName = node.getPrimaryNodeType().getName();
994 if(!defaultLanguage.equals(language)){
995 Node languagesNode = null ;
996 try {
997 languagesNode = node.getNode(LANGUAGES) ;
998 } catch(PathNotFoundException pe) {
999 languagesNode = node.addNode(LANGUAGES, NTUNSTRUCTURED) ;
1000 if(languagesNode.canAddMixin("exo:hiddenable"))
1001 languagesNode.addMixin("exo:hiddenable");
1002 }
1003 Node selectedLangNode = languagesNode.getNode(language) ;
1004 Node newLang = null;
1005 if(nodeTypeName.equals(NTFILE)) {
1006 Node jcrContentNode = node.getNode(JCRCONTENT) ;
1007 if(!jcrContentNode.getProperty(JCR_MIMETYPE).getString().startsWith("text")) {
1008 newLang = languagesNode.addNode(defaultLanguage);
1009 selectedLangNode = getFileLangNode(selectedLangNode) ;
1010 node.save();
1011 newLang = addNewFileNode(node.getName(), newLang, jcrContentNode.getProperty(JCRDATA).getValue(),
1012 new GregorianCalendar(), jcrContentNode.getProperty(JCR_MIMETYPE).getString(), repositoryName) ;
1013 Node newJcrContent = newLang.getNode(JCRCONTENT) ;
1014 newJcrContent.setProperty(JCRDATA, jcrContentNode.getProperty(JCRDATA).getValue()) ;
1015 newJcrContent.setProperty(JCR_MIMETYPE, jcrContentNode.getProperty(JCR_MIMETYPE).getString()) ;
1016 } else {
1017 newLang = languagesNode.addNode(defaultLanguage, nodeTypeName) ;
1018 }
1019 } else if(node.isNodeType(NTUNSTRUCTURED) || node.isNodeType(NTFOLDER)) {
1020 newLang = languagesNode.addNode(defaultLanguage);
1021 selectedLangNode = selectedLangNode.getNode(node.getName());
1022 newLang = newLang.addNode(node.getName(), nodeTypeName);
1023 } else {
1024 newLang = languagesNode.addNode(defaultLanguage, nodeTypeName);
1025 }
1026
1027 PropertyDefinition[] properties = node.getPrimaryNodeType().getPropertyDefinitions() ;
1028 for(PropertyDefinition pro : properties){
1029 if(!pro.isProtected()){
1030 String propertyName = pro.getName() ;
1031 if(node.hasProperty(propertyName)) {
1032 if(node.getProperty(propertyName).getDefinition().isMultiple()) {
1033 Value[] values = node.getProperty(propertyName).getValues() ;
1034 newLang.setProperty(propertyName, values) ;
1035 } else {
1036 newLang.setProperty(propertyName, node.getProperty(propertyName).getValue()) ;
1037 }
1038 }
1039 if(selectedLangNode.hasProperty(propertyName)) {
1040 if(selectedLangNode.getProperty(propertyName).getDefinition().isMultiple()) {
1041 Value[] values = selectedLangNode.getProperty(propertyName).getValues() ;
1042 node.setProperty(propertyName, values) ;
1043 } else {
1044 node.setProperty(propertyName, selectedLangNode.getProperty(propertyName).getValue()) ;
1045 }
1046 }
1047 }
1048 }
1049 setMixin(node, newLang);
1050 if(nodeTypeName.equals(NTFILE)) {
1051 Node tempNode = node.addNode(TEMP_NODE, NTUNSTRUCTURED) ;
1052 node.getSession().move(node.getNode(JCRCONTENT).getPath(), tempNode.getPath() + "/" + JCRCONTENT) ;
1053 node.getSession().move(selectedLangNode.getNode(JCRCONTENT).getPath(), node.getPath() + "/" + JCRCONTENT) ;
1054 if(node.getNode(JCRCONTENT).getProperty(JCR_MIMETYPE).getString().startsWith("text")) {
1055 node.getSession().move(tempNode.getPath() + "/" + JCRCONTENT, newLang.getPath() + "/" + JCRCONTENT);
1056 }
1057 tempNode.remove() ;
1058 } else if(node.isNodeType(NTUNSTRUCTURED) || node.isNodeType(NTFOLDER)) {
1059 processFolderNode(node, selectedLangNode, newLang);
1060 } else if(hasNodeTypeNTResource(node)) {
1061 processWithDataChildNode(node, selectedLangNode, languagesNode, defaultLanguage, getChildNodeType(node)) ;
1062 }
1063 setVoteProperty(newLang, node, selectedLangNode) ;
1064 node.setProperty(EXO_LANGUAGE, language) ;
1065 setCommentNode(node, newLang, selectedLangNode) ;
1066 if(nodeTypeName.equals(NTFILE) || node.isNodeType(NTUNSTRUCTURED) ||
1067 node.isNodeType(NTFOLDER)) {
1068 languagesNode.getNode(language).remove() ;
1069 } else {
1070 selectedLangNode.remove() ;
1071 }
1072 node.save() ;
1073 node.getSession().save() ;
1074 }
1075 }
1076
1077
1078
1079
1080
1081
1082
1083
1084 private void processFolderNode(Node node, Node selectedLangNode,
1085 Node newLang) throws RepositoryException {
1086 NodeIterator nodeIter = node.getNodes();
1087 while(nodeIter.hasNext()) {
1088 Node child = nodeIter.nextNode();
1089 if(child.getName().equals(LANGUAGES)) continue;
1090 if(!node.getSession().itemExists(newLang.getPath() + "/" + child.getName()))
1091 node.getSession().move(child.getPath(), newLang.getPath() + "/" + child.getName());
1092 }
1093 NodeIterator selectedIter = selectedLangNode.getNodes();
1094 while(selectedIter.hasNext()) {
1095 Node child = selectedIter.nextNode();
1096 if(!node.getSession().itemExists(node.getPath() + "/" + child.getName()))
1097 node.getSession().move(child.getPath(), node.getPath() + "/" + child.getName());
1098 }
1099 }
1100
1101
1102
1103
1104
1105
1106
1107 private void processFolderNode(Node node, Node newLang) throws RepositoryException {
1108 NodeIterator nodeIter = node.getNodes();
1109 Node tempNode = newLang.addNode(TEMP_NODE, NTUNSTRUCTURED);
1110 Node selectedLangNode = newLang.getNode(node.getName());
1111 while(nodeIter.hasNext()) {
1112 Node child = nodeIter.nextNode();
1113 if(child.getName().equals(LANGUAGES)) continue;
1114 if(!node.getSession().itemExists(tempNode.getPath() + "/" + child.getName()))
1115 node.getSession().move(child.getPath(), tempNode.getPath() + "/" + child.getName());
1116 }
1117
1118 NodeIterator selectedIter = selectedLangNode.getNodes();
1119 while(selectedIter.hasNext()) {
1120 Node child = selectedIter.nextNode();
1121 node.getSession().move(child.getPath(), node.getPath() + "/" + child.getName());
1122 }
1123 NodeIterator tempIter = tempNode.getNodes();
1124 while(tempIter.hasNext()) {
1125 Node child = tempIter.nextNode();
1126 if(!node.getSession().itemExists(selectedLangNode.getPath() + "/" + child.getName()))
1127 node.getSession().move(child.getPath(), selectedLangNode.getPath() + "/" + child.getName());
1128 }
1129 tempNode.remove();
1130 }
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142 private void processWithDataChildNode(Node node, Node selectedLangNode, Node languagesNode,
1143 String defaultLanguage, String nodeType) throws Exception {
1144 Node tempNode = node.addNode(TEMP_NODE, NTUNSTRUCTURED) ;
1145 if(!node.getSession().itemExists(tempNode.getPath() + "/" + nodeType))
1146 node.getSession().move(node.getNode(nodeType).getPath(), tempNode.getPath() + "/" + nodeType) ;
1147 if(!node.getSession().itemExists(node.getPath() + "/" + nodeType))
1148 node.getSession().move(selectedLangNode.getNode(nodeType).getPath(), node.getPath() + "/" + nodeType) ;
1149 if(!node.getSession().itemExists(languagesNode.getPath() + "/" + defaultLanguage + "/" + nodeType))
1150 node.getSession().move(tempNode.getNode(nodeType).getPath(),
1151 languagesNode.getPath() + "/" + defaultLanguage + "/" + nodeType);
1152 tempNode.remove() ;
1153 }
1154
1155
1156
1157
1158
1159
1160
1161
1162 private boolean hasNodeTypeNTResource(Node node) throws Exception {
1163 if(node.hasNodes()) {
1164 NodeIterator nodeIter = node.getNodes() ;
1165 while(nodeIter.hasNext()) {
1166 Node childNode = nodeIter.nextNode() ;
1167 if(childNode.isNodeType("nt:resource")) return true ;
1168 }
1169 }
1170 return false ;
1171 }
1172
1173
1174
1175
1176
1177
1178
1179
1180 private String getChildNodeType(Node node) throws Exception {
1181 if(node.hasNodes()) {
1182 NodeIterator nodeIter = node.getNodes() ;
1183 while(nodeIter.hasNext()) {
1184 Node childNode = nodeIter.nextNode() ;
1185 if(childNode.isNodeType("nt:resource")) return childNode.getName() ;
1186 }
1187 }
1188 return null ;
1189 }
1190
1191
1192
1193
1194 public Node getLanguage(Node node, String language) throws Exception {
1195 if(node.hasNode(LANGUAGES + "/"+ language)) {
1196 Node target = node.getNode(LANGUAGES + "/"+ language) ;
1197 if (target.isNodeType("exo:symlink")) {
1198 LinkManager linkManager = WCMCoreUtils.getService(LinkManager.class);
1199 target = linkManager.getTarget(target);
1200 }
1201 return target;
1202 }
1203 if (language.contains(COUNTRY_VARIANT)) {
1204 String pureLanguage = language.substring(0, language.indexOf(COUNTRY_VARIANT) ) ;
1205 if(node.hasNode(LANGUAGES + "/"+ pureLanguage)) {
1206 Node target = node.getNode(LANGUAGES + "/"+ pureLanguage) ;
1207 if (target.isNodeType("exo:symlink")) {
1208 LinkManager linkManager = WCMCoreUtils.getService(LinkManager.class);
1209 target = linkManager.getTarget(target);
1210 }
1211 return target;
1212 }
1213 }
1214 return null;
1215 }
1216
1217 public void addFolderLanguage(Node node, Map inputs, String language,
1218 boolean isDefault, String nodeType, String repositoryName) throws Exception {
1219 Node newLanguageNode = null ;
1220 Node languagesNode = null ;
1221 String defaultLanguage = getDefault(node) ;
1222 boolean isAddNew = false;
1223 if(node.hasNode(LANGUAGES)) {
1224 languagesNode = node.getNode(LANGUAGES) ;
1225 } else {
1226 languagesNode = node.addNode(LANGUAGES, NTUNSTRUCTURED) ;
1227 if(languagesNode.canAddMixin("exo:hiddenable")) languagesNode.addMixin("exo:hiddenable");
1228 }
1229 if(!defaultLanguage.equals(language)){
1230 String addedLange = language;
1231 if(isDefault) addedLange = defaultLanguage;
1232 try {
1233 isAddNew = false;
1234 newLanguageNode = languagesNode.getNode(addedLange) ;
1235 } catch(PathNotFoundException e) {
1236 isAddNew = true;
1237 newLanguageNode = languagesNode.addNode(addedLange) ;
1238 }
1239 }
1240 String nodePath = cmsService_.storeNode(nodeType, newLanguageNode, inputs, isAddNew);
1241 Node selectedNode = (Node)node.getSession().getItem(nodePath);
1242 if(isAddNew) {
1243 setMixin(node, selectedNode, false);
1244 selectedNode.setProperty(EXO_LANGUAGE, language) ;
1245 }
1246 setPropertyLanguage(node, selectedNode, inputs, isDefault, defaultLanguage, language);
1247 if(isDefault) processFolderNode(node, newLanguageNode);
1248 node.getSession().save();
1249 if(isDefault && languagesNode.hasNode(language)) languagesNode.getNode(language).remove() ;
1250 languagesNode.save();
1251 }
1252 }