001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.relations;
029
030import org.opencms.i18n.CmsMessages;
031import org.opencms.main.CmsIllegalArgumentException;
032import org.opencms.main.CmsInitException;
033import org.opencms.main.OpenCms;
034
035import java.io.Serializable;
036import java.util.ArrayList;
037import java.util.Arrays;
038import java.util.Collection;
039import java.util.Collections;
040import java.util.Iterator;
041import java.util.List;
042import java.util.Locale;
043
044/**
045 * Wrapper class for
046 * the different types of relations.<p>
047 *
048 * The possibles values are:<br>
049 * <ul>
050 *   <li>{@link #HYPERLINK}</li>
051 *   <li>{@link #EMBEDDED_IMAGE}</li>
052 *   <li>{@link #EMBEDDED_OBJECT}</li>
053 *   <li>{@link #XML_STRONG}</li>
054 *   <li>{@link #XML_WEAK}</li>
055 *   <li>{@link #JSP_STRONG}</li>
056 *   <li>{@link #JSP_WEAK}</li>
057 *   <li>{@link #OU_RESOURCE}</li>
058 *   <li>{@link #CATEGORY}</li>
059 *   <li>{@link #XSD}</li>
060 * </ul>
061 * <p>
062 *
063 * User defined relation types are also available.<p>
064 *
065 * @since 6.3.0
066 */
067public final class CmsRelationType implements Serializable {
068
069    /**
070     * Enum representing how relations should be handled while copying resources.<p>
071     */
072    public enum CopyBehavior {
073        /** Copy the relation when copying a resource. */
074        copy,
075
076        /** Ignore the relation when copying a resource. */
077        ignore;
078    }
079
080    // the following strings must not be public because they confuse the interface
081    // this means we can't sort this class members according to standard
082    /** String prefix for 'JSP relations. */
083    private static final String PREFIX_JSP = "JSP_";
084
085    /** String prefix for XML relations. */
086    private static final String PREFIX_XML = "XML_";
087
088    /** String constant for "STRONG" relations. */
089    private static final String VALUE_STRONG = "STRONG";
090
091    /** String constant for "WEAK" relations. */
092    private static final String VALUE_WEAK = "WEAK";
093
094    /** Constant for the category of an <code>OpenCmsVfsFile</code>. */
095    public static final CmsRelationType CATEGORY = new CmsRelationType(9, "CATEGORY", false, false, CopyBehavior.copy);
096
097    /** Constant for the <code>&lt;img src=''&gt;</code> tag in a html page/element. */
098    public static final CmsRelationType EMBEDDED_IMAGE = new CmsRelationType(2, "IMG", true, true, CopyBehavior.copy);
099
100    /** Constant for the <code>&lt;embed src=''&gt;</code> tag in a html page/element. */
101    public static final CmsRelationType EMBEDDED_OBJECT = new CmsRelationType(
102        7,
103        "OBJECT",
104        true,
105        true,
106        CopyBehavior.copy);
107
108    /** Constant for the <code>&lt;a href=''&gt;</code> tag in a html page/element. */
109    public static final CmsRelationType HYPERLINK = new CmsRelationType(1, "A", false, true, CopyBehavior.copy);
110
111    /** Constant for the index content relation, telling the content of a linked resource should be merged into
112     * the content of the linking XML.
113     */
114    public static final CmsRelationType INDEX_CONTENT = new CmsRelationType(
115        13,
116        "INDEX_CONTENT",
117        true,
118        true,
119        CopyBehavior.copy);
120
121    /** Constant for the all types of links in a jsp file using the <code>link.strong</code> macro. */
122    public static final CmsRelationType JSP_STRONG = new CmsRelationType(
123        5,
124        PREFIX_JSP + VALUE_STRONG,
125        true,
126        true,
127        CopyBehavior.copy);
128
129    /** Constant for the all types of links in a jsp file using the <code>link.weak</code> macro. */
130    public static final CmsRelationType JSP_WEAK = new CmsRelationType(
131        6,
132        PREFIX_JSP + VALUE_WEAK,
133        false,
134        true,
135        CopyBehavior.copy);
136
137    /** Constant for the organizational units resource associations. */
138    public static final CmsRelationType OU_RESOURCE = new CmsRelationType(8, "OU", false, false, CopyBehavior.copy);
139
140    /** Constant for the <code>OpenCmsVfsFile</code> values in xml content that were defined as 'strong' links. */
141    public static final CmsRelationType XML_STRONG = new CmsRelationType(
142        3,
143        PREFIX_XML + VALUE_STRONG,
144        true,
145        true,
146        CopyBehavior.copy);
147
148    /** Constant for the <code>OpenCmsVfsFile</code> values in xml content that were defined as 'weak' links. */
149    public static final CmsRelationType XML_WEAK = new CmsRelationType(
150        4,
151        PREFIX_XML + VALUE_WEAK,
152        false,
153        true,
154        CopyBehavior.copy);
155
156    /** Constant for the type of relations between resources which are locale variants. */
157    public static final CmsRelationType LOCALE_VARIANT = new CmsRelationType(
158        11,
159        "LOCALE_VARIANT",
160        false,
161        false,
162        CopyBehavior.ignore);
163
164    /** Constant for the type of relations between a detail content and its detail-only container pages. */
165    public static final CmsRelationType DETAIL_ONLY = new CmsRelationType(
166        12,
167        "DETAIL_ONLY",
168        true,
169        false,
170        CopyBehavior.ignore);
171
172    /** Constant for the weak links from xmlcontent to the used xsd. */
173    public static final CmsRelationType XSD = new CmsRelationType(10, "XSD", true, true, CopyBehavior.copy);
174
175    /** Serial version UID required for safe serialization. */
176    private static final long serialVersionUID = -4060567973007877250L;
177
178    /** Constant indicating the starting mode for user defined relation types. */
179    private static final int USER_DEFINED_MODE_LIMIT = 100;
180
181    /** Array constant for all available system relation types. */
182    private static final CmsRelationType[] VALUE_ARRAY = {
183        HYPERLINK,
184        EMBEDDED_IMAGE,
185        XML_STRONG,
186        XML_WEAK,
187        JSP_STRONG,
188        JSP_WEAK,
189        EMBEDDED_OBJECT,
190        OU_RESOURCE,
191        CATEGORY,
192        XSD,
193        LOCALE_VARIANT,
194        DETAIL_ONLY,
195        INDEX_CONTENT};
196
197    /** The copy behavior. */
198    private CopyBehavior m_copyBehavior = CopyBehavior.copy;
199
200    /** Flag to indicate if the relations of this type are parsed from the content or not. */
201    private final boolean m_defInContent;
202
203    /** Internal representation. */
204    private final int m_id;
205
206    /** Some name for this relation type, ie. for &lt;link&gt; tag representation. */
207    private final String m_name;
208
209    /** Flag to indicate if the relations of this type are strong or weak. */
210    private final boolean m_strong;
211
212    /**
213     * Public constructor for user defined relation types.<p>
214     *
215     * @param id the id of the relation type
216     * @param name the name of the relation
217     * @param type the type of relation type, strong or weak
218     */
219    public CmsRelationType(int id, String name, String type) {
220
221        m_name = name.toUpperCase();
222        if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_2_INITIALIZING) {
223            // allow relation type definitions only during startup
224            throw new CmsInitException(Messages.get().container(Messages.ERR_RELATION_TYPE_INIT_1, m_name));
225        }
226        m_strong = type.toUpperCase().equals(VALUE_STRONG);
227        m_defInContent = false;
228        m_id = USER_DEFINED_MODE_LIMIT + id;
229    }
230
231    /**
232     * Private constructor for system relation types.<p>
233     *
234     * @param id the internal representation
235     * @param name the name of the relation
236     * @param strong if the relation is strong or weak
237     * @param defInContent <code>true</code> if the link is defined in the content
238     * @param copyBehavior the copy behavior of the content
239     */
240    private CmsRelationType(int id, String name, boolean strong, boolean defInContent, CopyBehavior copyBehavior) {
241
242        m_id = id;
243        m_name = name;
244        m_strong = strong;
245        m_defInContent = defInContent;
246        m_copyBehavior = copyBehavior;
247    }
248
249    /**
250     * Returns all relation types in the given list that define relations in the content.<p>
251     *
252     * @param relationTypes the collection of relation types to filter
253     *
254     * @return a list of {@link CmsRelationType} objects
255     */
256    public static List<CmsRelationType> filterDefinedInContent(Collection<CmsRelationType> relationTypes) {
257
258        List<CmsRelationType> result = new ArrayList<CmsRelationType>(relationTypes);
259        Iterator<CmsRelationType> it = result.iterator();
260        while (it.hasNext()) {
261            CmsRelationType type = it.next();
262            if (!type.isDefinedInContent()) {
263                it.remove();
264            }
265        }
266        return result;
267    }
268
269    /**
270     * Returns all internal defined relation types in the given list.<p>
271     *
272     * @param relationTypes the collection of relation types to filter
273     *
274     * @return a list of {@link CmsRelationType} objects
275     */
276    public static List<CmsRelationType> filterInternal(Collection<CmsRelationType> relationTypes) {
277
278        List<CmsRelationType> result = new ArrayList<CmsRelationType>(relationTypes);
279        Iterator<CmsRelationType> it = result.iterator();
280        while (it.hasNext()) {
281            CmsRelationType type = it.next();
282            if (!type.isInternal()) {
283                it.remove();
284            }
285        }
286        return result;
287    }
288
289    /**
290     * Returns all relation types in the given list that are not defined in the content.<p>
291     *
292     * @param relationTypes the collection of relation types to filter
293     *
294     * @return a list of {@link CmsRelationType} objects
295     */
296    public static List<CmsRelationType> filterNotDefinedInContent(Collection<CmsRelationType> relationTypes) {
297
298        List<CmsRelationType> result = new ArrayList<CmsRelationType>(relationTypes);
299        Iterator<CmsRelationType> it = result.iterator();
300        while (it.hasNext()) {
301            CmsRelationType type = it.next();
302            if (type.isDefinedInContent()) {
303                it.remove();
304            }
305        }
306        return result;
307    }
308
309    /**
310     * Returns all strong relation types in the given list.<p>
311     *
312     * @param relationTypes the collection of relation types to filter
313     *
314     * @return a list of {@link CmsRelationType} objects
315     */
316    public static List<CmsRelationType> filterStrong(Collection<CmsRelationType> relationTypes) {
317
318        List<CmsRelationType> result = new ArrayList<CmsRelationType>(relationTypes);
319        Iterator<CmsRelationType> it = result.iterator();
320        while (it.hasNext()) {
321            CmsRelationType type = it.next();
322            if (!type.isStrong()) {
323                it.remove();
324            }
325        }
326        return result;
327    }
328
329    /**
330     * Returns all user defined relation types in the given list.<p>
331     *
332     * @param relationTypes the collection of relation types to filter
333     *
334     * @return a list of {@link CmsRelationType} objects
335     */
336    public static List<CmsRelationType> filterUserDefined(Collection<CmsRelationType> relationTypes) {
337
338        List<CmsRelationType> result = new ArrayList<CmsRelationType>(relationTypes);
339        Iterator<CmsRelationType> it = result.iterator();
340        while (it.hasNext()) {
341            CmsRelationType type = it.next();
342            if (type.isInternal()) {
343                it.remove();
344            }
345        }
346        return result;
347    }
348
349    /**
350     * Returns all weak relation types in the given list.<p>
351     *
352     * @param relationTypes the collection of relation types to filter
353     *
354     * @return a list of {@link CmsRelationType} objects
355     */
356    public static List<CmsRelationType> filterWeak(Collection<CmsRelationType> relationTypes) {
357
358        List<CmsRelationType> result = new ArrayList<CmsRelationType>(relationTypes);
359        Iterator<CmsRelationType> it = result.iterator();
360        while (it.hasNext()) {
361            CmsRelationType type = it.next();
362            if (type.isStrong()) {
363                it.remove();
364            }
365        }
366        return result;
367    }
368
369    /**
370     * Returns all relation types.<p>
371     *
372     * @return a list of {@link CmsRelationType} objects
373     */
374    public static List<CmsRelationType> getAll() {
375
376        List<CmsRelationType> all = new ArrayList<CmsRelationType>(Arrays.asList(VALUE_ARRAY));
377        all.addAll(OpenCms.getResourceManager().getRelationTypes());
378        return Collections.unmodifiableList(all);
379    }
380
381    /**
382     * Returns all relation types for relations defined in the content.<p>
383     *
384     * @return a list of {@link CmsRelationType} objects
385     */
386    public static List<CmsRelationType> getAllDefinedInContent() {
387
388        return filterDefinedInContent(getAll());
389    }
390
391    /**
392     * Returns all internally defined relation types.<p>
393     *
394     * @return a list of {@link CmsRelationType} objects
395     */
396    public static List<CmsRelationType> getAllInternal() {
397
398        return Collections.unmodifiableList(Arrays.asList(VALUE_ARRAY));
399    }
400
401    /**
402     * Returns all relation types for relations that are not defined in the content.<p>
403     *
404     * @return a list of {@link CmsRelationType} objects
405     */
406    public static List<CmsRelationType> getAllNotDefinedInContent() {
407
408        return filterNotDefinedInContent(getAll());
409    }
410
411    /**
412     * Returns all strong relation types.<p>
413     *
414     * @return a list of {@link CmsRelationType} objects
415     */
416    public static List<CmsRelationType> getAllStrong() {
417
418        return filterStrong(getAll());
419    }
420
421    /**
422     * Returns all user defined relation types.<p>
423     *
424     * @return a list of {@link CmsRelationType} objects
425     */
426    public static List<CmsRelationType> getAllUserDefined() {
427
428        return OpenCms.getResourceManager().getRelationTypes();
429    }
430
431    /**
432     * Returns all weak relation types.<p>
433     *
434     * @return a list of {@link CmsRelationType} objects
435     */
436    public static List<CmsRelationType> getAllWeak() {
437
438        return filterWeak(getAll());
439    }
440
441    /**
442     * Parses an <code>int</code> into a relation type.<p>
443     *
444     * @param id the internal representation number to parse
445     *
446     * @return the enumeration element
447     *
448     * @throws CmsIllegalArgumentException if the given value could not be matched against a
449     *         <code>{@link CmsRelationType}</code> object.
450     */
451    public static CmsRelationType valueOf(int id) throws CmsIllegalArgumentException {
452
453        if ((id > 0) && (id <= VALUE_ARRAY.length)) {
454            return VALUE_ARRAY[id - 1];
455        }
456        id -= USER_DEFINED_MODE_LIMIT;
457        if ((id >= 0) && (id < getAllUserDefined().size())) {
458            return getAllUserDefined().get(id);
459        }
460        throw new CmsIllegalArgumentException(
461            org.opencms.db.Messages.get().container(
462                org.opencms.db.Messages.ERR_MODE_ENUM_PARSE_2,
463                Integer.valueOf(id),
464                CmsRelationType.class.getName()));
465    }
466
467    /**
468     * Parses an <code>String</code> into a relation type.<p>
469     *
470     * @param name the relation type name
471     *
472     * @return the enumeration element
473     *
474     * @throws CmsIllegalArgumentException if the given value could not be matched against a
475     *         <code>{@link CmsRelationType}</code> object
476     *
477     * @see #valueOfXml(String)
478     * @see #valueOfJsp(String)
479     */
480    public static CmsRelationType valueOf(String name) throws CmsIllegalArgumentException {
481
482        CmsRelationType result = valueOfInternal(name);
483        if (result == null) {
484            // no type found
485            throw new CmsIllegalArgumentException(
486                org.opencms.db.Messages.get().container(
487                    org.opencms.db.Messages.ERR_MODE_ENUM_PARSE_2,
488                    name,
489                    CmsRelationType.class.getName()));
490        }
491        return result;
492    }
493
494    /**
495     * Parses the given value into a valid enumeration element for a JSP relation type.<p>
496     *
497     * This should be used to extend Strings like "weak" or "strong" to full relation type descriptors
498     * for JSP pages like "JSP_WEAK" or "JSP_STRONG".<p>
499     *
500     * @param name the name to get the JSP type for
501     *
502     * @return the JSP enumeration element
503     *
504     * @see #valueOf(String)
505     */
506    public static CmsRelationType valueOfJsp(String name) {
507
508        CmsRelationType result = valueOfInternal(name);
509        if (result == null) {
510            result = valueOf(PREFIX_JSP + name);
511        }
512        return result;
513    }
514
515    /**
516     * Parses the given value into a valid enumeration element for a XML relation type.<p>
517     *
518     * This should be used to extend Strings like "weak" or "strong" to full relation type descriptors
519     * for XML documents like "XML_WEAK" or "XML_STRONG".<p>
520     *
521     * @param name the name to get the XML type for
522     *
523     * @return the XML enumeration element
524     *
525     * @see #valueOf(String)
526     */
527    public static CmsRelationType valueOfXml(String name) {
528
529        CmsRelationType result = valueOfInternal(name);
530        if (result == null) {
531            result = valueOf(PREFIX_XML + name);
532        }
533        return result;
534    }
535
536    /**
537     * Internal parse method.<p>
538     *
539     * @param name the type to parse
540     *
541     * @return the enumeration element, or <code>null</code> if no matching element is found
542     */
543    private static CmsRelationType valueOfInternal(String name) {
544
545        if (name != null) {
546            String valueUp = name.toUpperCase();
547            for (int i = 0; i < VALUE_ARRAY.length; i++) {
548                if (valueUp.equals(VALUE_ARRAY[i].m_name)) {
549                    return VALUE_ARRAY[i];
550                }
551            }
552            // deprecated types
553            if (valueUp.equals("REFERENCE") || valueUp.equals("XML_REFERENCE")) {
554                return XML_WEAK;
555            } else if (valueUp.equals("ATTACHMENT") || valueUp.equals("XML_ATTACHMENT")) {
556                return XML_STRONG;
557            }
558            // user defined
559            for (int i = 0; i < getAllUserDefined().size(); i++) {
560                CmsRelationType type = getAllUserDefined().get(i);
561                if (valueUp.equals(type.m_name)) {
562                    return type;
563                }
564            }
565        }
566        return null;
567    }
568
569    /**
570     * @see java.lang.Object#equals(java.lang.Object)
571     */
572    @Override
573    public boolean equals(Object obj) {
574
575        if (this == obj) {
576            return true;
577        }
578        if (obj instanceof CmsRelationType) {
579            return (m_id == ((CmsRelationType)obj).m_id);
580        }
581        return false;
582    }
583
584    /**
585     * Gets the 'copy behavior' of the relation type, which is how relations of a resource should be handled when copying that resource.<p>
586     *
587     * @return the copy behavior of the relation type
588     */
589    public CopyBehavior getCopyBehavior() {
590
591        return m_copyBehavior;
592    }
593
594    /**
595     * Returns the internal representation of this type.<p>
596     *
597     * @return the internal representation of this type
598     */
599    public int getId() {
600
601        return m_id;
602    }
603
604    /**
605     * Returns a localized name for the given relation type.<p>
606     *
607     * @param messages the message bundle to use to resolve the name
608     *
609     * @return a localized name
610     */
611    public String getLocalizedName(CmsMessages messages) {
612
613        String nameKey = "GUI_RELATION_TYPE_" + getName() + "_0";
614        return messages.key(nameKey);
615    }
616
617    /**
618     * Returns a localized name for the given relation type.<p>
619     *
620     * @param locale the locale
621     *
622     * @return a localized name
623     */
624    public String getLocalizedName(Locale locale) {
625
626        return getLocalizedName(Messages.get().getBundle(locale));
627    }
628
629    /**
630     * Returns the type name.<p>
631     *
632     * @return the type name
633     *
634     * @see CmsRelationType#valueOf(String)
635     */
636    public String getName() {
637
638        return m_name;
639    }
640
641    /**
642     * Returns the type name for xml output.<p>
643     *
644     * The short type name of XML or JSP types is only <code>"WEAK"</code> or <code>"STRONG"</code>.
645     * For other types the short name is equal to the name.<p>
646     *
647     * In case you need the full type name, use {@link #getName()}.<p>
648     *
649     * @return the short type name
650     *
651     * @see #getName()
652     * @see CmsRelationType#valueOfJsp(String)
653     * @see CmsRelationType#valueOfXml(String)
654     */
655    public String getNameForXml() {
656
657        String result;
658        switch (getId()) {
659            case 3: // xml strong
660                result = VALUE_STRONG;
661                break;
662            case 4: // xml weak
663                result = VALUE_WEAK;
664                break;
665            case 5: // jsp strong
666                result = VALUE_STRONG;
667                break;
668            case 6: // jsp weak
669                result = VALUE_WEAK;
670                break;
671            default:
672                result = getName();
673        }
674        return result;
675    }
676
677    /**
678     * Returns the string strong or weak.<p>
679     *
680     * @return the string strong or weak
681     *
682     * @see #isStrong()
683     */
684    public String getType() {
685
686        return isStrong() ? VALUE_STRONG : VALUE_WEAK;
687    }
688
689    /**
690     * @see java.lang.Object#hashCode()
691     */
692    @Override
693    public int hashCode() {
694
695        return m_id;
696    }
697
698    /**
699     * Checks if this relation type is defined in the content of a resource or not.<p>
700     *
701     * @return <code>true</code> if this relation type is defined in the content of a resource
702     */
703    public boolean isDefinedInContent() {
704
705        return m_defInContent;
706    }
707
708    /**
709     * Checks if this is an internal relation type.<p>
710     *
711     * @return <code>true</code> if this is an internal relation type
712     */
713    public boolean isInternal() {
714
715        return (getId() < USER_DEFINED_MODE_LIMIT);
716    }
717
718    /**
719     * Checks if the relation type is strong or weak.<p>
720     *
721     * @return <code>true</code> if the relation type is strong
722     */
723    public boolean isStrong() {
724
725        return m_strong;
726    }
727
728    /**
729     * @see java.lang.Object#toString()
730     */
731    @Override
732    public String toString() {
733
734        return m_name;
735    }
736}