001/*
002 *  Copyright 2011 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.odf.enumeration;
017
018import java.util.Collections;
019import java.util.HashMap;
020import java.util.HashSet;
021import java.util.LinkedHashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.Set;
025import java.util.stream.Collectors;
026
027import org.apache.avalon.framework.component.Component;
028import org.apache.avalon.framework.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030import org.apache.avalon.framework.service.Serviceable;
031import org.apache.cocoon.xml.AttributesImpl;
032import org.apache.cocoon.xml.XMLUtils;
033import org.apache.commons.lang3.StringUtils;
034import org.xml.sax.ContentHandler;
035import org.xml.sax.SAXException;
036
037import org.ametys.cms.contenttype.ContentAttributeDefinition;
038import org.ametys.cms.contenttype.ContentType;
039import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
040import org.ametys.cms.contenttype.ContentTypesHelper;
041import org.ametys.cms.repository.Content;
042import org.ametys.cms.repository.ContentQueryHelper;
043import org.ametys.cms.repository.ContentTypeExpression;
044import org.ametys.core.ui.Callable;
045import org.ametys.plugins.repository.AmetysObjectIterable;
046import org.ametys.plugins.repository.AmetysObjectIterator;
047import org.ametys.plugins.repository.AmetysObjectResolver;
048import org.ametys.plugins.repository.UnknownAmetysObjectException;
049import org.ametys.plugins.repository.model.RepeaterDefinition;
050import org.ametys.plugins.repository.query.SortCriteria;
051import org.ametys.plugins.repository.query.expression.AndExpression;
052import org.ametys.plugins.repository.query.expression.Expression.Operator;
053import org.ametys.plugins.repository.query.expression.StringExpression;
054import org.ametys.runtime.config.Config;
055import org.ametys.runtime.model.ElementDefinition;
056import org.ametys.runtime.model.ModelItem;
057import org.ametys.runtime.model.ModelItemContainer;
058import org.ametys.runtime.model.View;
059import org.ametys.runtime.model.ViewElement;
060import org.ametys.runtime.model.ViewItem;
061import org.ametys.runtime.model.ViewItemContainer;
062import org.ametys.runtime.plugin.component.AbstractLogEnabled;
063
064/**
065 * This component handles ODF reference tables 
066 *
067 */
068public class OdfReferenceTableHelper extends AbstractLogEnabled implements Component, Serviceable
069{
070    /** Avalon Role */
071    public static final String ROLE = OdfReferenceTableHelper.class.getName();
072 
073    /** Abstract table ref */
074    public static final String ABSTRACT_TABLE_REF = "odf-enumeration.AbstractTableRef";
075    /** Domain */
076    public static final String DOMAIN = "odf-enumeration.Domain";
077    /** Degree */
078    public static final String DEGREE = "odf-enumeration.Degree";
079    /** Level */
080    public static final String LEVEL = "odf-enumeration.Level";
081    /** Program type. */
082    public static final String PROGRAM_TYPE = "odf-enumeration.ProgramType";
083    /** Form of teaching */
084    public static final String FORMOFTEACHING_METHOD = "odf-enumeration.FormofteachingMethod";
085    /** Orgnization of teaching */
086    public static final String FORMOFTEACHING_ORG = "odf-enumeration.FormofteachingOrg";
087    /** Teaching method */
088    public static final String TEACHING_ACTIVITY = "odf-enumeration.TeachingActivity";
089    /** Type of training course */
090    public static final String INTERNSHIP = "odf-enumeration.Internship";
091    /** Distance learning modalities */
092    public static final String DISTANCE_LEARNING_MODALITIES = "odf-enumeration.DistanceLearningModalities";
093    /** Place */
094    public static final String PLACE = "odf-enumeration.Place";
095    /** Teaching term. */
096    public static final String TEACHING_TERM = "odf-enumeration.TeachingTerm";
097    /** Time slot */
098    public static final String TIME_SLOT = "odf-enumeration.TimeSlot";
099    /** Code ROME */
100    public static final String CODE_ROME = "odf-enumeration.CodeRome";
101    /** Code ERASMUS */
102    public static final String CODE_ERASMUS = "odf-enumeration.CodeErasmus";
103    /** Code DGESIP */
104    public static final String CODE_DGESIP = "odf-enumeration.CodeDgesip";
105    /** Code SISE */
106    public static final String CODE_SISE = "odf-enumeration.CodeSise";
107    /** Code Cite97 */
108    public static final String CODE_CITE97 = "odf-enumeration.CodeCite97";
109    /** Code FAP */
110    public static final String CODE_FAP = "odf-enumeration.CodeFap";
111    /** Code NSF */
112    public static final String CODE_NSF = "odf-enumeration.CodeNsf";
113    /** RNCP level */
114    public static final String RNCP_LEVEL = "odf-enumeration.RncpLevel";
115    /** Join orgunit*/
116    public static final String JOIN_ORGUNIT = "odf-enumeration.JoinOrgunit";
117    /** Mention licence */
118    public static final String ABSTRACT_MENTION = "odf-enumeration.Mention";
119    /** Mention licence */
120    public static final String MENTION_LICENCE = "odf-enumeration.MentionLicence";
121    /** Mention licence pro */
122    public static final String MENTION_LICENCEPRO = "odf-enumeration.MentionLicencepro";
123    /** Mention master */
124    public static final String MENTION_MASTER = "odf-enumeration.MentionMaster";
125    /** Abstract table ref for category */
126    public static final String ABSTRACT_TABLE_REF_CATEGORY = "odf-enumeration.AbstractTableRefCategory";
127    /** Apprenticeship contract */
128    public static final String APPRENTICESHIP_CONTRACT = "odf-enumeration.ApprenticeshipContract";
129    /** Available certification */
130    public static final String AVAILABLE_CERTIFICATION = "odf-enumeration.AvailableCertification";
131    /** Campus */
132    public static final String CAMPUS = "odf-enumeration.Campus";
133    /** Category for code Erasmus */
134    public static final String CODE_ERASMUS_CATEGORY = "odf-enumeration.CodeErasmusCategory";
135    /** Category for code FAP */
136    public static final String CODE_FAP_CATEGORY = "odf-enumeration.CodeFapCategory";
137    /** Nature of container */
138    public static final String CONTAINER_NATURE = "odf-enumeration.ContainerNature";
139    /** Nature of course */
140    public static final String COURSE_NATURE = "odf-enumeration.CourseNature";
141    /** Discipline */
142    public static final String DISCIPLINE = "odf-enumeration.Discipline";
143    /** Duration */
144    public static final String DURATION = "odf-enumeration.Duration";
145    /** ECTS */
146    public static final String ECTS = "odf-enumeration.Ects";
147    /** Foreign place */
148    public static final String FOREIGN_PLACE = "odf-enumeration.ForeignPlace";
149    /** International education */
150    public static final String INTERNATIONAL_EDUCATION = "odf-enumeration.InternationalEducation";
151    /** Language */
152    public static final String LANGUAGE = "odf-enumeration.Language";
153    /** OrgUnit type */
154    public static final String ORGUNIT_TYPE = "odf-enumeration.OrgUnitType";
155    /** Period */
156    public static final String PERIOD = "odf-enumeration.Period";
157    /** Period type */
158    public static final String PERIOD_TYPE = "odf-enumeration.PeriodType";
159    /** Person role */
160    public static final String PERSON_ROLE = "odf-enumeration.PersonRole";
161    /** Program field */
162    public static final String PROGRAM_FIELD = "odf-enumeration.ProgramField";
163    /** Sectors */
164    public static final String SECTORS = "odf-enumeration.Sectors";
165    /** Nature of course part */
166    public static final String ENSEIGNEMENT_NATURE = "odf-enumeration.EnseignementNature";
167    /** Category of nature of course part */
168    public static final String ENSEIGNEMENT_NATURE_CATEGORY = "odf-enumeration.EnseignementNatureCategory";
169    /** Skill */
170    public static final String SKILL = "odf-enumeration.Skill";
171    /** Skill set */
172    public static final String SKILL_SET = "odf-enumeration.SkillSet";
173    
174    private AmetysObjectResolver _resolver;
175
176    private ContentTypeExtensionPoint _cTypeEP;
177
178    private ContentTypesHelper _cTypeHelper;
179
180    @Override
181    public void service(ServiceManager manager) throws ServiceException
182    {
183        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
184        _cTypeEP = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE);
185        _cTypeHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE);
186    } 
187    
188    /**
189     * Determines if the content type is a ODF table reference
190     * @param cTypeId The id of content type
191     * @return true if the content type is a ODF table reference
192     */
193    public boolean isTableReference(String cTypeId)
194    {
195        return _cTypeHelper.getAncestors(cTypeId).contains(ABSTRACT_TABLE_REF);
196    }
197    
198    /**
199     * Determines if the content is an entry of a ODF table reference
200     * @param content The content 
201     * @return <code>true</code> if the content is an entry of a ODF table reference
202     */
203    public boolean isTableReferenceEntry(Content content)
204    {
205        String[] cTypeIds = content.getTypes();
206        for (String cTypeId : cTypeIds)
207        {
208            if (isTableReference(cTypeId))
209            {
210                return true;
211            }
212        }
213        return false;
214    }
215    
216    /**
217     * Get the id of table references
218     * @return The content type's id
219     */
220    public Set<String> getTableReferenceIds()
221    {
222        Set<String> tableRefIds = new HashSet<>();
223        
224        for (String cTypeId : _cTypeEP.getExtensionsIds())
225        {
226            if (_cTypeHelper.getAncestors(cTypeId).contains(ABSTRACT_TABLE_REF))
227            {
228                tableRefIds.add(cTypeId);
229            }
230        }
231        return tableRefIds;
232    }
233    
234    /**
235     * Get the attribute definitions for table references for a given content type
236     * @param cTypeId The id of content type
237     * @return The attribute definitions for table references
238     */
239    public Map<String, ContentAttributeDefinition> getTableRefAttributeDefinitions(String cTypeId)
240    {
241        ContentType cType = _cTypeEP.getExtension(cTypeId);
242        return _getTableRefAttributes(cType);
243    }
244    
245    private Map<String, ContentAttributeDefinition> _getTableRefAttributes(ModelItemContainer modelItemContainer)
246    {
247        Map<String, ContentAttributeDefinition> tableRefAttributes = new LinkedHashMap<>();
248        
249        for (ModelItem modelItem : modelItemContainer.getModelItems())
250        {
251            if (modelItem instanceof ContentAttributeDefinition)
252            {
253                ContentAttributeDefinition attributeDefinition = (ContentAttributeDefinition) modelItem;
254                if (isTableReference(attributeDefinition.getContentTypeId()))
255                {
256                    tableRefAttributes.put(attributeDefinition.getPath(), attributeDefinition);
257                }
258            }
259            else if (modelItem instanceof ModelItemContainer && !(modelItem instanceof RepeaterDefinition))
260            {
261                // enumerated attributes in repeaters are not supported
262                tableRefAttributes.putAll(_getTableRefAttributes((ModelItemContainer) modelItem));
263            }
264        }
265        
266        return tableRefAttributes;
267    }
268    
269    /**
270     * Get the attribute definitions for table references for a given content type and a view
271     * @param cTypeId The id of content type
272     * @param viewName the name of the view. Cannot be null.
273     * @return The attributes definitions for table references
274     */
275    public Map<String, ContentAttributeDefinition> getTableRefAttributeDefinitions(String cTypeId, String viewName)
276    {
277        ContentType cType = _cTypeEP.getExtension(cTypeId);
278        
279        View view = cType.getView(viewName);
280        return _getTableRefAttributes(view);
281    }
282    
283    private Map<String, ContentAttributeDefinition> _getTableRefAttributes(ViewItemContainer viewItemContainer)
284    {
285        Map<String, ContentAttributeDefinition> tableRefAttributes = new LinkedHashMap<>();
286        
287        for (ViewItem viewItem : viewItemContainer.getViewItems())
288        {
289            if (viewItem instanceof ViewElement)
290            {
291                ElementDefinition definition = ((ViewElement) viewItem).getDefinition();
292                if (definition instanceof ContentAttributeDefinition)
293                {
294                    tableRefAttributes.put(definition.getPath(), (ContentAttributeDefinition) definition);
295                }
296            }
297            else if (viewItem instanceof ViewItemContainer)
298            {
299                tableRefAttributes.putAll(_getTableRefAttributes((ViewItemContainer) viewItem));
300            }
301        }
302        
303        return tableRefAttributes;
304    }
305    
306    /**
307     * Get the content type for mention for this degree
308     * @param degreeIds The ids of degrees
309     * @return A map with the id of content type or null if there is no mention for this degree
310     */
311    @Callable
312    public Map<String, String> getMentionContentTypes(List<String> degreeIds)
313    {
314        Map<String, String> mentionTypes = new HashMap<>();
315        for (String degreeId : degreeIds)
316        {
317            mentionTypes.put(degreeId, getMentionForDegree(degreeId));
318        }
319        return mentionTypes;
320    }
321    
322    /**
323     * Get the mention for a given degree.
324     * @param degreeId The degree ID
325     * @return The associated mention reference table, null if there isn't
326     */
327    public String getMentionForDegree(String degreeId)
328    {
329        try
330        {
331            Content content = _resolver.resolveById(degreeId);
332            OdfReferenceTableEntry entry = new OdfReferenceTableEntry(content);
333            
334            String degreeCode = entry.getCode();
335            
336            if (degreeCode.equals(Config.getInstance().getValue("odf.programs.degree.license")))
337            {
338                return OdfReferenceTableHelper.MENTION_LICENCE;
339            }
340            else if (degreeCode.equals(Config.getInstance().getValue("odf.programs.degree.licensepro")))
341            {
342                return OdfReferenceTableHelper.MENTION_LICENCEPRO;
343            }
344            else if (degreeCode.equals(Config.getInstance().getValue("odf.programs.degree.master")))
345            {
346                return OdfReferenceTableHelper.MENTION_MASTER;
347            }
348        }
349        catch (UnknownAmetysObjectException e)
350        {
351            // Nothing to do
352        }
353        return null;
354    }
355    
356    /**
357     * Get the CDM-fr value associated with the given code
358     * @param tableRefId The id of content type
359     * @param code The code
360     * @return The CDM-fr value or empty string if not found
361     */
362    public String getCDMfrValue (String tableRefId, String code)
363    {
364        ContentTypeExpression cTypeExpr = new ContentTypeExpression(Operator.EQ, tableRefId);
365        StringExpression codeExpr = new StringExpression(OdfReferenceTableEntry.CODE, Operator.EQ, code);
366        
367        String xpathQuery = ContentQueryHelper.getContentXPathQuery(new AndExpression(cTypeExpr, codeExpr));
368        AmetysObjectIterable<Content> contents = _resolver.query(xpathQuery);
369        AmetysObjectIterator<Content> it = contents.iterator();
370        
371        if (it.hasNext())
372        {
373            OdfReferenceTableEntry entry = new OdfReferenceTableEntry(it.next());
374            return entry.getCdmValue();
375        }
376        
377        return "";
378    }
379    
380    /**
381     * Get all items of an enumeration and their label
382     * @param tableRefId The id of content type
383     * @return items of enumeration
384     */
385    public List<OdfReferenceTableEntry> getItems (String tableRefId)
386    {
387        return getItems(tableRefId, new SortField[0]);
388    }
389    
390    /**
391     * Get all items of an enumeration and their label, sorted by given fields
392     * @param tableRefId The id of content type
393     * @param sortFields The sort fields to order results
394     * @return items of enumeration
395     */
396    public List<OdfReferenceTableEntry> getItems (String tableRefId, SortField... sortFields)
397    {
398        ContentTypeExpression cTypeExpr = new ContentTypeExpression(Operator.EQ, tableRefId);
399        
400        SortCriteria sortCriteria = new SortCriteria();
401        for (SortField sortField : sortFields)
402        {
403            sortCriteria.addCriterion(sortField.getName(), sortField.getAscending(), sortField.getNormalize());
404        }
405        
406        String xpathQuery = ContentQueryHelper.getContentXPathQuery(cTypeExpr, sortCriteria);
407        AmetysObjectIterable<Content> contents = _resolver.query(xpathQuery);
408        
409        return contents.stream().map(c -> new OdfReferenceTableEntry(c)).collect(Collectors.toList());
410    }
411    
412    /**
413     * Returns the label of an reference table entry
414     * @param contentId The content id
415     * @param lang The requested language of label
416     * @return the item label or <code>null</code> if not found
417     */
418    public String getItemLabel(String contentId, String lang)
419    {
420        try
421        {
422            Content content = _resolver.resolveById(contentId);
423            OdfReferenceTableEntry entry = new OdfReferenceTableEntry(content);
424            return entry.getLabel(lang);
425        }
426        catch (UnknownAmetysObjectException e)
427        {
428            return null;
429        }
430    }
431    
432    /**
433     * Returns the label of an reference table entry
434     * @param tableRefId The id of content type (useless)
435     * @param contentId The content id
436     * @param lang The requested language of label
437     * @return the item label or <code>null</code> if not found
438     * @deprecated Use {@link #getItemLabel(String, String)} instead
439     */
440    @Deprecated
441    public String getItemLabel (String tableRefId, String contentId, String lang)
442    {
443        return getItemLabel(contentId, lang);
444    }
445
446    /**
447     * Returns the CMD value of an reference table entry
448     * @param contentId The content id
449     * @param returnCodeIfEmpty <code>true</code> to return the code if CDM-fr value is empty
450     * @return the CDM-fr value or empty value if not found
451     */
452    public String getItemCDMfrValue(String contentId, boolean returnCodeIfEmpty)
453    {
454        if (StringUtils.isEmpty(contentId))
455        {
456            return "";
457        }
458        
459        Content content = _resolver.resolveById(contentId);
460        OdfReferenceTableEntry entry = new OdfReferenceTableEntry(content);
461        
462        String cdmValue = entry.getCdmValue();
463        
464        if (StringUtils.isEmpty(cdmValue) && returnCodeIfEmpty)
465        {
466            return entry.getCode();
467        }
468        return cdmValue;
469    }
470    
471    /**
472     * Returns the CMD value of an reference table entry
473     * @param tableRefId The id of content type (useless)
474     * @param contentId The content id
475     * @param returnCodeIfEmpty <code>true</code> to return the code if CDM-fr value is empty
476     * @return the CDM-fr value or empty value if not found
477     * @deprecated Use {@link #getItemCDMfrValue(String, boolean)} instead
478     */
479    @Deprecated
480    public String getItemCDMfrValue (String tableRefId, String contentId, boolean returnCodeIfEmpty)
481    {
482        return getItemCDMfrValue(contentId, returnCodeIfEmpty);
483    }
484    
485    /**
486     * Returns the code of an reference table entry from its CDM value
487     * @param contentId The id of content
488     * @return the code or empty value if not found
489     */
490    public String getItemCode(String contentId)
491    {
492        if (StringUtils.isEmpty(contentId))
493        {
494            return "";
495        }
496        
497        try
498        {
499            Content content = _resolver.resolveById(contentId);
500            OdfReferenceTableEntry entry = new OdfReferenceTableEntry(content);
501            return entry.getCode();
502        }
503        catch (UnknownAmetysObjectException e)
504        {
505            return "";
506        }
507    }
508    
509    /**
510     * Returns the code of an reference table entry from its CDM value
511     * @param tableRefId The id of content type (useless)
512     * @param contentId The id of content
513     * @return the code or empty value if not found
514     * @deprecated Use {@link #getItemCode(String)} instead
515     */
516    @Deprecated
517    public String getItemCode (String tableRefId, String contentId)
518    {
519        return getItemCode(contentId);
520    }
521    
522    /**
523     * Returns the code of an reference table entry from its CDM value
524     * @param tableRefId The id of content type
525     * @param cdmValue The CDM-fr value
526     * @return the code or <code>null</code> if not found
527     */
528    public String getItemCodeFromCDM (String tableRefId, String cdmValue)
529    {
530        ContentTypeExpression cTypeExpr = _getContentTypeExpression(tableRefId);
531        StringExpression codeExpr = new StringExpression(OdfReferenceTableEntry.CDM_VALUE, Operator.EQ, cdmValue);
532        
533        String xpathQuery = ContentQueryHelper.getContentXPathQuery(new AndExpression(cTypeExpr, codeExpr));
534        AmetysObjectIterable<Content> contents = _resolver.query(xpathQuery);
535        AmetysObjectIterator<Content> it = contents.iterator();
536        
537        if (it.hasNext())
538        {
539            OdfReferenceTableEntry entry = new OdfReferenceTableEntry(it.next());
540            return entry.getCode();
541        }
542        return null;
543    }
544
545    /**
546     * Returns the entry of an reference table entry from its cdmValue
547     * @param tableRefId The id of content type
548     * @param cdmValue The CDM-fr value
549     * @return the entry or <code>null</code> if not found
550     */
551    public OdfReferenceTableEntry getItemFromCDM(String tableRefId, String cdmValue)
552    {
553        ContentTypeExpression cTypeExpr = _getContentTypeExpression(tableRefId);
554        StringExpression cdmExpr = new StringExpression(OdfReferenceTableEntry.CDM_VALUE, Operator.EQ, cdmValue);
555        
556        String xpathQuery = ContentQueryHelper.getContentXPathQuery(new AndExpression(cTypeExpr, cdmExpr));
557
558        return _resolver.<Content>query(xpathQuery).stream()
559            .findFirst()
560            .map(OdfReferenceTableEntry::new)
561            .orElse(null);
562    }
563    
564    /**
565     * Returns the entry of an reference table entry from its code
566     * @param tableRefId The id of content type
567     * @param code The code
568     * @return the entry or <code>null</code> if not found
569     */
570    public OdfReferenceTableEntry getItemFromCode(String tableRefId, String code)
571    {
572        ContentTypeExpression cTypeExpr = _getContentTypeExpression(tableRefId);
573        StringExpression codeExpr = new StringExpression(OdfReferenceTableEntry.CODE, Operator.EQ, code);
574        
575        String xpathQuery = ContentQueryHelper.getContentXPathQuery(new AndExpression(cTypeExpr, codeExpr));
576        
577        return _resolver.<Content>query(xpathQuery).stream()
578            .findFirst()
579            .map(OdfReferenceTableEntry::new)
580            .orElse(null);
581    }
582    
583    private ContentTypeExpression _getContentTypeExpression(String tableRefId)
584    {
585        Set<String> tableRefids;
586        if (tableRefId.equals(OdfReferenceTableHelper.ABSTRACT_MENTION))
587        {
588            tableRefids = Set.of(OdfReferenceTableHelper.MENTION_LICENCE, OdfReferenceTableHelper.MENTION_LICENCEPRO, OdfReferenceTableHelper.MENTION_MASTER);
589        }
590        else
591        {
592            tableRefids = Collections.singleton(tableRefId);
593        }
594        
595        return new ContentTypeExpression(Operator.EQ, tableRefids.toArray(new String[tableRefids.size()]));
596    }
597
598    /**
599     * Returns the reference table entry from its CDM value
600     * @param contentId The id of content
601     * @return the item as an {@link OdfReferenceTableEntry} or null if not found
602     */
603    public OdfReferenceTableEntry getItem(String contentId)
604    {
605        try
606        {
607            Content content = _resolver.resolveById(contentId);
608            return new OdfReferenceTableEntry(content);
609        }
610        catch (UnknownAmetysObjectException e)
611        {
612            // Can be an empty ID or an invalid ID (workspace or simply deleted element)
613            return null;
614        }
615    }
616    
617    /**
618     * SAX items of a reference table
619     * @param contentHandler The content handler to sax into
620     * @param tableRefId The id of reference table
621     * @throws SAXException if an error occurred while saxing
622     */
623    public void saxItems (ContentHandler contentHandler, String tableRefId) throws SAXException
624    {
625        _saxItems(contentHandler, new AttributesImpl(), tableRefId);
626    }
627    
628    /**
629     * SAX items of a reference table
630     * @param contentHandler the content handler to sax into
631     * @param attributeDefinition the metadata definition
632     * @throws SAXException if an error occurs while saxing
633     */
634    public void saxItems (ContentHandler contentHandler, ContentAttributeDefinition attributeDefinition) throws SAXException
635    {
636        String cTypeId = attributeDefinition.getContentTypeId();
637        if (cTypeId.startsWith("odf-enumeration."))
638        {
639            AttributesImpl attrs = new AttributesImpl();
640            attrs.addCDATAAttribute("metadataName", attributeDefinition.getName());
641            attrs.addCDATAAttribute("metadataPath", attributeDefinition.getPath());
642            
643            _saxItems(contentHandler, attrs, cTypeId);
644        }
645    }
646    
647    private void _saxItems (ContentHandler contentHandler, AttributesImpl rootAttrs, String tableRefId) throws SAXException
648    {
649        rootAttrs.addCDATAAttribute("contentTypeId", tableRefId);
650        XMLUtils.startElement(contentHandler, "items", rootAttrs);
651        
652        List<OdfReferenceTableEntry> entries = getItems(tableRefId);
653        for (OdfReferenceTableEntry entry : entries)
654        {
655            AttributesImpl valueAttrs = new AttributesImpl();
656            valueAttrs.addCDATAAttribute("id", entry.getId());
657            valueAttrs.addCDATAAttribute("order", entry.getOrder().toString());
658            valueAttrs.addCDATAAttribute("code", entry.getCode());
659            valueAttrs.addCDATAAttribute("cdmValue", entry.getCdmValue());
660            
661            String lang = Config.getInstance().getValue("odf.programs.lang");
662            XMLUtils.createElement(contentHandler, "item", valueAttrs, entry.getLabel(lang));
663        }
664        
665        XMLUtils.endElement(contentHandler, "items");
666    }
667    
668    /**
669     * This class represents a sort field for reference table.
670     *
671     */
672    public static final class SortField
673    {
674        private String _name;
675        private boolean _ascending;
676        private boolean _normalize;
677
678        /**
679         * Create a sort field
680         * @param name the name of field to sort on
681         * @param ascending <code>true</code> to sort in ascending order, <code>false</code> otherwise
682         */
683        public SortField(String name, boolean ascending)
684        {
685            this(name, ascending, false);
686        }
687        
688        /**
689         * Create a sort field
690         * @param name the name of field to sort on
691         * @param ascending <code>true</code> to sort in ascending order, <code>false</code> otherwise
692         * @param normalize <code>true</code> to normalize string properties (remove accents and lower case)
693         */
694        public SortField(String name, boolean ascending, boolean normalize)
695        {
696            _name = name;
697            _ascending = ascending;
698            _normalize = normalize;
699        }
700
701        /**
702         * Get the name of the sort field
703         * @return the name of the sort field
704         */
705        public String getName()
706        {
707            return _name;
708        }
709
710        /**
711         * Get the order for sorting results
712         * @return <code>true</code> to sort results in ascending order, <code>false</code> otherwise
713         */
714        public boolean getAscending()
715        {
716            return _ascending;
717        }
718        
719        /**
720         * Return the normalize status for this sort field
721         * @return <code>true</code> if string properties should be normalized (remove accents and lower case)
722         */
723        public boolean getNormalize()
724        {
725            return _normalize;
726        }
727
728    }
729}