001/*
002 *  Copyright 2010 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.plugins.repository.workspace;
017
018import java.io.IOException;
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.Collection;
022import java.util.Collections;
023import java.util.Date;
024import java.util.Iterator;
025import java.util.List;
026
027import javax.jcr.Node;
028import javax.jcr.RepositoryException;
029
030import org.apache.avalon.framework.service.ServiceException;
031import org.apache.avalon.framework.service.ServiceManager;
032import org.apache.cocoon.ProcessingException;
033import org.apache.cocoon.generation.ServiceableGenerator;
034import org.apache.cocoon.xml.AttributesImpl;
035import org.apache.cocoon.xml.XMLUtils;
036import org.apache.commons.lang3.StringUtils;
037import org.xml.sax.Attributes;
038import org.xml.sax.SAXException;
039
040import org.ametys.core.util.DateUtils;
041import org.ametys.plugins.repository.AmetysObject;
042import org.ametys.plugins.repository.AmetysObjectIterable;
043import org.ametys.plugins.repository.AmetysObjectResolver;
044import org.ametys.plugins.repository.ModifiableAmetysObject;
045import org.ametys.plugins.repository.ModifiableTraversableAmetysObject;
046import org.ametys.plugins.repository.TraversableAmetysObject;
047import org.ametys.plugins.repository.jcr.JCRAmetysObject;
048import org.ametys.plugins.repository.lock.LockAwareAmetysObject;
049import org.ametys.plugins.repository.lock.LockableAmetysObject;
050import org.ametys.plugins.repository.metadata.CompositeMetadata;
051import org.ametys.plugins.repository.metadata.CompositeMetadata.MetadataType;
052import org.ametys.plugins.repository.metadata.File;
053import org.ametys.plugins.repository.metadata.Folder;
054import org.ametys.plugins.repository.metadata.MetadataAwareAmetysObject;
055import org.ametys.plugins.repository.metadata.RichText;
056import org.ametys.workspaces.repository.jcr.NodeGroupHelper;
057
058/**
059 * Generate the content of a logic node:
060 * <ul>
061 *  <li>metadata
062 *  <li>ametys object properties
063 *  <li>children
064 * </ul>
065 */
066public class RepositoryLogicNodeGenerator extends ServiceableGenerator
067{
068    private AmetysObjectResolver _resolver;
069    
070    @Override
071    public void service(ServiceManager serviceManager) throws ServiceException
072    {
073        super.service(serviceManager);
074        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
075    }
076    
077    public void generate() throws IOException, SAXException, ProcessingException
078    {
079        if (getLogger().isInfoEnabled())
080        {
081            getLogger().info("Trying to generate content for a node");
082        }
083
084        String id = parameters.getParameter("id", "");
085        
086        contentHandler.startDocument();
087        AttributesImpl repositoryAttributes = new AttributesImpl();
088
089        XMLUtils.startElement(contentHandler, "repository", repositoryAttributes);
090
091        try
092        {
093            _saxObject(id);
094        }
095        catch (RepositoryException e)
096        {
097            throw new ProcessingException("Unable to retrieve node for path: '" + source + "'", e);
098        }
099
100        XMLUtils.endElement(contentHandler, "repository");
101
102        contentHandler.endDocument();
103    }
104    
105    private void _saxObject(String id) throws SAXException, RepositoryException
106    {
107        AmetysObject ao = null;
108        
109        if (StringUtils.isEmpty(id) || id.equals("/"))
110        {
111            ao = _resolver.resolveByPath("/");
112        }
113        else
114        {
115            ao = _resolver.resolveById(id);
116        }
117        
118        boolean isModifiable = ao instanceof ModifiableAmetysObject;
119        boolean isLocked = ao instanceof LockAwareAmetysObject && ((LockAwareAmetysObject) ao).isLocked();
120        AttributesImpl nodeAttributes = new AttributesImpl();
121
122        nodeAttributes.addCDATAAttribute("path", ao.getPath());
123        nodeAttributes.addCDATAAttribute("name", ao.getName());
124        nodeAttributes.addCDATAAttribute("id", ao.getId());
125
126        XMLUtils.startElement(contentHandler, "ametysObject", nodeAttributes);
127
128        Attributes attrs = XMLUtils.EMPTY_ATTRIBUTES;
129        // MetaData
130        if (ao instanceof MetadataAwareAmetysObject)
131        {
132            XMLUtils.createElement(contentHandler, "isMetadataAware", attrs, "true");
133            CompositeMetadata holder = ((MetadataAwareAmetysObject) ao).getMetadataHolder();
134            _saxMetaData(holder, isLocked, isModifiable);
135        }
136        else
137        {
138            XMLUtils.createElement(contentHandler, "isMetadataAware", attrs, "false");
139        }
140
141        // Children
142        if (ao instanceof TraversableAmetysObject)
143        {
144            _saxChildren((TraversableAmetysObject) ao);
145        }
146
147        if (ao instanceof JCRAmetysObject)
148        {
149            Node node = ((JCRAmetysObject) ao).getNode();
150            XMLUtils.createElement(contentHandler, "isJCRAO", attrs, "true");
151            XMLUtils.createElement(contentHandler, "JCRPath", attrs, node.getPath());
152            XMLUtils.createElement(contentHandler, "JCRPathWithGroups", attrs, NodeGroupHelper.getPathWithGroups(node));
153        }
154        else
155        {
156            XMLUtils.createElement(contentHandler, "isJCRAO", attrs, "false");
157        }
158
159        XMLUtils.createElement(contentHandler, "isModifiable", attrs, Boolean.toString(isModifiable));
160        XMLUtils.createElement(contentHandler, "isModifiableTraversable", attrs, Boolean.toString(ao instanceof ModifiableTraversableAmetysObject));
161        
162        if (ao instanceof LockAwareAmetysObject)
163        {
164            XMLUtils.createElement(contentHandler, "isLocked", attrs, Boolean.toString(isLocked));
165            XMLUtils.createElement(contentHandler, "isLockable", attrs, Boolean.toString(ao instanceof LockableAmetysObject));
166        }
167        else
168        {
169            XMLUtils.createElement(contentHandler, "isLocked", attrs, "not-applicable");
170            XMLUtils.createElement(contentHandler, "isLockable", attrs, "false");
171        }
172        
173
174        XMLUtils.endElement(contentHandler, "ametysObject");
175    }
176
177    private void _saxMetaData(CompositeMetadata holder, boolean isLocked, boolean isModifiable) throws SAXException, RepositoryException
178    {        
179        String[] metadataNames = holder.getMetadataNames();
180        
181        List<String> metaNames = new ArrayList<>(Arrays.asList(metadataNames));
182        Collections.sort(metaNames);
183        
184        for (String name : metaNames)
185        {
186            AttributesImpl metadataAttributes = new AttributesImpl();
187
188            MetadataType type = holder.getType(name);
189            Boolean isMultiple = holder.isMultiple(name);
190
191            metadataAttributes.addCDATAAttribute("name", name);
192            metadataAttributes.addCDATAAttribute("type", type.toString());
193            metadataAttributes.addCDATAAttribute("multiple", Boolean.toString(isMultiple));
194            metadataAttributes.addCDATAAttribute("editable", Boolean.toString(type != MetadataType.COMPOSITE && isModifiable && !isLocked));
195            
196            XMLUtils.startElement(contentHandler, "metadata", metadataAttributes);
197
198            _saxValues(holder, name, type, isMultiple, isLocked, isModifiable);
199
200            XMLUtils.endElement(contentHandler, "metadata");
201        }
202
203    }
204
205    private void _saxChildren(TraversableAmetysObject ao) throws SAXException, RepositoryException
206    {        
207        AmetysObjectIterable<AmetysObject> children = ao.getChildren();
208        Iterator<AmetysObject> it = children.iterator();
209
210        while (it.hasNext())
211        {
212            AmetysObject child = it.next();
213
214            String id = child.getId();
215            String name = child.getName();
216
217            AttributesImpl childAttributes = new AttributesImpl();
218            childAttributes.addCDATAAttribute("name", name);
219            childAttributes.addCDATAAttribute("id", id);
220            childAttributes.addCDATAAttribute("path", child.getPath());
221            
222            if (child instanceof JCRAmetysObject)
223            {
224                Node node = ((JCRAmetysObject) child).getNode();
225                childAttributes.addCDATAAttribute("isJCRAO", "true");
226                childAttributes.addCDATAAttribute("JCRPath", node.getPath());
227                childAttributes.addCDATAAttribute("JCRPathWithGroups", NodeGroupHelper.getPathWithGroups(node));
228            }
229            
230            // If not traversable or no child
231            if (!(child instanceof TraversableAmetysObject))
232            {
233                childAttributes.addCDATAAttribute("noChild", "true");
234            }
235            else if (!((TraversableAmetysObject) child).getChildren().iterator().hasNext())
236            {
237                childAttributes.addCDATAAttribute("noChild", "true");
238            }
239
240            XMLUtils.createElement(contentHandler, "ametysObject", childAttributes);
241        }
242    }
243
244    private void _saxValues(CompositeMetadata holder, String name, MetadataType type, Boolean isMultiple, boolean isLocked, boolean isModifiable) throws RepositoryException, SAXException
245    {
246        if (isMultiple)
247        {
248            _saxMultipleValues(holder, name, type, isLocked, isModifiable);
249        }
250        else
251        {
252            _saxSimpleValue(holder, name, type, isLocked, isModifiable);
253        }
254    }
255
256    private void _saxMultipleValues(CompositeMetadata holder, String name, MetadataType type, boolean isLocked, boolean isModifiable) throws RepositoryException, SAXException
257    {
258        Attributes attrs = XMLUtils.EMPTY_ATTRIBUTES;
259        String textValue = null;
260
261        switch (type)
262        {
263            case DATE:
264                Date[] dateValues = holder.getDateArray(name);
265                for (Date dVal : dateValues)
266                {
267                    XMLUtils.createElement(contentHandler, "value", attrs, DateUtils.dateToString(dVal));
268                }
269                break;
270            case STRING:
271                String[] stringValues = holder.getStringArray(name);
272                for (String sVal : stringValues)
273                {
274                    textValue = sVal;
275                    XMLUtils.createElement(contentHandler, "value", attrs, textValue);
276                }
277                break;
278            case LONG:
279                long[] longValues = holder.getLongArray(name);
280                for (long lVal : longValues)
281                {
282                    textValue = Long.toString(lVal);
283                    XMLUtils.createElement(contentHandler, "value", attrs, textValue);
284                }
285                break;
286            case DOUBLE:
287                double[] doubleValues = holder.getDoubleArray(name);
288                for (double dbVal : doubleValues)
289                {
290                    textValue = Double.toString(dbVal);
291                    XMLUtils.createElement(contentHandler, "value", attrs, textValue);
292                }
293                break;
294            case BOOLEAN:
295                boolean[] booleanValues = holder.getBooleanArray(name);
296                for (boolean bVal : booleanValues)
297                {
298                    textValue = Boolean.toString(bVal);
299                    XMLUtils.createElement(contentHandler, "value", attrs, textValue);
300                }
301                break;
302            case COMPOSITE:
303                CompositeMetadata cm = holder.getCompositeMetadata(name);
304                _saxMetaData(cm, isLocked, isModifiable);
305                break;
306            case BINARY:
307                String mimeType = holder.getBinaryMetadata(name).getMimeType();
308                AttributesImpl attrsimpl = new AttributesImpl();
309                attrsimpl.addCDATAAttribute("mime-type", mimeType);
310                XMLUtils.createElement(contentHandler, "value", attrsimpl);
311                break;
312            case RICHTEXT:
313                RichText rt = holder.getRichText(name);
314                _saxRichText(rt);
315                break;
316            case OBJECT_COLLECTION:
317                // TODO
318                break;
319            default:
320                textValue = "";
321                XMLUtils.createElement(contentHandler, "value", attrs, textValue);
322                break;
323        }
324
325    }
326
327    private void _saxSimpleValue(CompositeMetadata holder, String name, MetadataType type, boolean isLocked, boolean isModifiable) throws RepositoryException, SAXException
328    {
329        Attributes attrs = XMLUtils.EMPTY_ATTRIBUTES;
330        String textValue = null;
331
332        switch (type)
333        {
334            case DATE:
335                XMLUtils.createElement(contentHandler, "value", attrs, DateUtils.dateToString(holder.getDate(name)));
336                break;
337            case STRING:
338                textValue = holder.getString(name);
339                XMLUtils.createElement(contentHandler, "value", attrs, textValue);
340                break;
341            case LONG:
342                textValue = Long.toString(holder.getLong(name));
343                XMLUtils.createElement(contentHandler, "value", attrs, textValue);
344                break;
345            case DOUBLE:
346                textValue = Double.toString(holder.getDouble(name));
347                XMLUtils.createElement(contentHandler, "value", attrs, textValue);
348                break;
349            case BOOLEAN:
350                textValue = Boolean.toString(holder.getBoolean(name));
351                XMLUtils.createElement(contentHandler, "value", attrs, textValue);
352                break;
353            case COMPOSITE:
354                CompositeMetadata cm = holder.getCompositeMetadata(name);
355                _saxMetaData(cm, isLocked, isModifiable);
356                break;
357            case BINARY:
358                String mimeType = holder.getBinaryMetadata(name).getMimeType();
359                AttributesImpl attrsimpl = new AttributesImpl();
360                attrsimpl.addCDATAAttribute("mime-type", mimeType);
361                XMLUtils.createElement(contentHandler, "value", attrsimpl);
362                break;
363            case RICHTEXT:
364                RichText rt = holder.getRichText(name);
365                _saxRichText(rt);
366                break;
367            case OBJECT_COLLECTION:
368                // TODO
369                break;
370            default:
371                textValue = "";
372                XMLUtils.createElement(contentHandler, "value", attrs, textValue);
373                break;
374        }
375    }
376
377    private void _saxRichText(RichText rt) throws RepositoryException, SAXException
378    {
379        //content
380        AttributesImpl contentAttributes = new AttributesImpl();
381        contentAttributes.addCDATAAttribute("name", "data");
382        contentAttributes.addCDATAAttribute("type", "RICHTEXT-CONTENT");
383        contentAttributes.addCDATAAttribute("isMultiple", "false");
384        XMLUtils.startElement(contentHandler, "metadata", contentAttributes);  
385        
386        AttributesImpl attrsimplRT = new AttributesImpl();
387        attrsimplRT.addCDATAAttribute("mime-type", rt.getMimeType());
388        XMLUtils.createElement(contentHandler, "value", attrsimplRT);
389        
390        XMLUtils.endElement(contentHandler, "metadata");
391        
392        //encoding
393        if (rt.getEncoding() != null)
394        {
395            AttributesImpl encodingAttributes = new AttributesImpl();
396            encodingAttributes.addCDATAAttribute("name", "encoding");
397            encodingAttributes.addCDATAAttribute("type", "STRING");
398            encodingAttributes.addCDATAAttribute("isMultiple", "false");
399            XMLUtils.startElement(contentHandler, "metadata", encodingAttributes);
400            
401            XMLUtils.createElement(contentHandler, "value", XMLUtils.EMPTY_ATTRIBUTES, rt.getEncoding());
402            
403            XMLUtils.endElement(contentHandler, "metadata");
404        }
405        
406        //mime-type
407        AttributesImpl mimeAttributes = new AttributesImpl();
408        mimeAttributes.addCDATAAttribute("name", "mime-type");
409        mimeAttributes.addCDATAAttribute("type", "STRING");
410        mimeAttributes.addCDATAAttribute("isMultiple", "false");
411        XMLUtils.startElement(contentHandler, "metadata", mimeAttributes);
412        
413        XMLUtils.createElement(contentHandler, "value", XMLUtils.EMPTY_ATTRIBUTES, rt.getMimeType());
414        
415        XMLUtils.endElement(contentHandler, "metadata");
416        
417        //last modified
418        AttributesImpl lastmodAttributes = new AttributesImpl();
419        lastmodAttributes.addCDATAAttribute("name", "last-modified-date");
420        lastmodAttributes.addCDATAAttribute("type", "DATE");
421        lastmodAttributes.addCDATAAttribute("isMultiple", "false");
422        XMLUtils.startElement(contentHandler, "metadata", lastmodAttributes);
423        
424        XMLUtils.createElement(contentHandler, "value", XMLUtils.EMPTY_ATTRIBUTES, DateUtils.dateToString(rt.getLastModified()));
425        
426        XMLUtils.endElement(contentHandler, "metadata");
427        
428        //length
429        AttributesImpl lengthAttributes = new AttributesImpl();
430        lengthAttributes.addCDATAAttribute("name", "length");
431        lengthAttributes.addCDATAAttribute("type", "LONG");
432        lengthAttributes.addCDATAAttribute("isMultiple", "false");
433        XMLUtils.startElement(contentHandler, "metadata", lengthAttributes);
434        
435        XMLUtils.createElement(contentHandler, "value", XMLUtils.EMPTY_ATTRIBUTES, Long.toString(rt.getLength()));
436        
437        XMLUtils.endElement(contentHandler, "metadata");
438        
439        //sub folders
440        _saxFolder(rt.getAdditionalDataFolder());
441    }
442
443    private void _saxFolder(Folder folder) throws RepositoryException, SAXException
444    {
445        AttributesImpl folderAttributes = new AttributesImpl();
446        folderAttributes.addCDATAAttribute("name", folder.getName());
447        folderAttributes.addCDATAAttribute("type", "FOLDER");
448        folderAttributes.addCDATAAttribute("isMultiple", "false");
449        XMLUtils.startElement(contentHandler, "metadata", folderAttributes);
450
451        Collection<? extends Folder> folders = folder.getFolders();
452        Iterator itFolders = folders.iterator();
453        while (itFolders.hasNext())
454        {
455            Folder subfold = (Folder) itFolders.next();
456            _saxFolder(subfold);
457        }
458
459        Collection<? extends File> files = folder.getFiles();
460        Iterator itFiles = files.iterator();
461        while (itFiles.hasNext())
462        {
463            File file = (File) itFiles.next();
464            _saxFile(file);
465        }
466
467        XMLUtils.endElement(contentHandler, "metadata");
468    }
469
470
471
472    private void _saxFile(File file) throws SAXException
473    {
474        AttributesImpl fileAttributes = new AttributesImpl();
475        fileAttributes.addCDATAAttribute("name", file.getName());
476        fileAttributes.addCDATAAttribute("type", "FILE");
477        fileAttributes.addCDATAAttribute("isMultiple", "false");
478        XMLUtils.startElement(contentHandler, "metadata", fileAttributes);
479
480        //content
481        AttributesImpl contentAttributes = new AttributesImpl();
482        contentAttributes.addCDATAAttribute("name", "data");
483        contentAttributes.addCDATAAttribute("type", "FILE-CONTENT");
484        contentAttributes.addCDATAAttribute("isMultiple", "false");
485        XMLUtils.startElement(contentHandler, "metadata", contentAttributes);  
486
487        AttributesImpl attrsimplF = new AttributesImpl();
488        attrsimplF.addCDATAAttribute("mime-type", file.getResource().getMimeType());
489        XMLUtils.createElement(contentHandler, "value", attrsimplF);
490
491        XMLUtils.endElement(contentHandler, "metadata");
492
493        //encoding
494        if (file.getResource().getEncoding() != null)
495        {
496            AttributesImpl encodingAttributes = new AttributesImpl();
497            encodingAttributes.addCDATAAttribute("name", "encoding");
498            encodingAttributes.addCDATAAttribute("type", "STRING");
499            encodingAttributes.addCDATAAttribute("isMultiple", "false");
500            XMLUtils.startElement(contentHandler, "metadata", encodingAttributes);
501
502            XMLUtils.createElement(contentHandler, "value", XMLUtils.EMPTY_ATTRIBUTES, file.getResource().getEncoding());
503
504            XMLUtils.endElement(contentHandler, "metadata");
505        }
506
507        //mime-type
508        AttributesImpl mimeAttributes = new AttributesImpl();
509        mimeAttributes.addCDATAAttribute("name", "mime-type");
510        mimeAttributes.addCDATAAttribute("type", "STRING");
511        mimeAttributes.addCDATAAttribute("isMultiple", "false");
512        XMLUtils.startElement(contentHandler, "metadata", mimeAttributes);
513
514        XMLUtils.createElement(contentHandler, "value", XMLUtils.EMPTY_ATTRIBUTES, file.getResource().getMimeType());
515
516        XMLUtils.endElement(contentHandler, "metadata");
517
518        //last modified
519        AttributesImpl lastmodAttributes = new AttributesImpl();
520        lastmodAttributes.addCDATAAttribute("name", "last-modified-date");
521        lastmodAttributes.addCDATAAttribute("type", "DATE");
522        lastmodAttributes.addCDATAAttribute("isMultiple", "false");
523        XMLUtils.startElement(contentHandler, "metadata", lastmodAttributes);
524
525        XMLUtils.createElement(contentHandler, "value", XMLUtils.EMPTY_ATTRIBUTES, DateUtils.dateToString(file.getResource().getLastModified()));
526
527        XMLUtils.endElement(contentHandler, "metadata");
528
529        //length
530        AttributesImpl lengthAttributes = new AttributesImpl();
531        lengthAttributes.addCDATAAttribute("name", "length");
532        lengthAttributes.addCDATAAttribute("type", "LONG");
533        lengthAttributes.addCDATAAttribute("isMultiple", "false");
534        XMLUtils.startElement(contentHandler, "metadata", lengthAttributes);
535
536        XMLUtils.createElement(contentHandler, "value", XMLUtils.EMPTY_ATTRIBUTES, Long.toString(file.getResource().getLength()));
537
538        XMLUtils.endElement(contentHandler, "metadata");
539
540        XMLUtils.endElement(contentHandler, "metadata");
541    }
542
543}