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, 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.xml.containerpage;
029
030import org.opencms.ade.configuration.CmsADEManager;
031import org.opencms.ade.containerpage.shared.CmsContainerElement;
032import org.opencms.ade.containerpage.shared.CmsContainerElement.ModelGroupState;
033import org.opencms.ade.containerpage.shared.CmsFormatterConfig;
034import org.opencms.ade.containerpage.shared.CmsInheritanceInfo;
035import org.opencms.file.CmsFile;
036import org.opencms.file.CmsObject;
037import org.opencms.file.CmsResource;
038import org.opencms.file.CmsResourceFilter;
039import org.opencms.file.types.CmsResourceTypeXmlContainerPage;
040import org.opencms.file.types.CmsResourceTypeXmlContent;
041import org.opencms.file.types.I_CmsResourceType;
042import org.opencms.main.CmsException;
043import org.opencms.main.OpenCms;
044import org.opencms.util.CmsNullIgnoringConcurrentMap;
045import org.opencms.util.CmsUUID;
046import org.opencms.xml.CmsXmlContentDefinition;
047import org.opencms.xml.content.CmsXmlContent;
048import org.opencms.xml.content.CmsXmlContentFactory;
049import org.opencms.xml.content.CmsXmlContentPropertyHelper;
050
051import java.util.Collections;
052import java.util.HashMap;
053import java.util.Locale;
054import java.util.Map;
055
056import javax.servlet.ServletRequest;
057
058/**
059 * One element of a container in a container page.<p>
060 *
061 * @since 8.0
062 */
063public class CmsContainerElementBean implements Cloneable {
064
065    /** Flag indicating if a new element should be created replacing the given one on first edit of a container-page. */
066    private final boolean m_createNew;
067
068    /** The client ADE editor hash. */
069    private transient String m_editorHash;
070
071    /** The element's structure id. */
072    private CmsUUID m_elementId;
073
074    /** The formatter's structure id. */
075    private CmsUUID m_formatterId;
076
077    /** The configured properties. */
078    private Map<String, String> m_individualSettings;
079
080    /** The inheritance info of this element. */
081    private CmsInheritanceInfo m_inheritanceInfo;
082
083    /** Indicates whether the represented resource is in memory only and not in the VFS. */
084    private boolean m_inMemoryOnly;
085
086    /** Indicating if the element resource is released and not expired. */
087    private boolean m_releasedAndNotExpired;
088
089    /** The resource of this element. */
090    private transient CmsResource m_resource;
091
092    /** The settings of this element containing also default values. */
093    private transient Map<String, String> m_settings;
094
095    /** The element site path, only set while rendering. */
096    private String m_sitePath;
097
098    /** Indicates the element bean has a temporary file content set. */
099    private boolean m_temporaryContent;
100
101    /**
102     * Creates a new container page element bean.<p>
103     *
104     * @param file the element's file
105     * @param formatterId the formatter's structure id, could be <code>null</code>
106     * @param individualSettings the element settings as a map of name/value pairs
107     * @param inMemoryOnly the in memory flag
108     * @param editorHash the editor hash to use
109     * @param createNew <code>true</code> if a new element should be created replacing the given one on first edit of a container-page
110     **/
111    public CmsContainerElementBean(
112        CmsFile file,
113        CmsUUID formatterId,
114        Map<String, String> individualSettings,
115        boolean inMemoryOnly,
116        String editorHash,
117        boolean createNew) {
118
119        this(file.getStructureId(), formatterId, individualSettings, createNew);
120        m_inMemoryOnly = inMemoryOnly;
121        m_editorHash = editorHash;
122        m_resource = file;
123    }
124
125    /**
126     * Creates a new container page element bean.<p>
127     *
128     * @param elementId the element's structure id
129     * @param formatterId the formatter's structure id, could be <code>null</code>
130     * @param individualSettings the element settings as a map of name/value pairs
131     * @param createNew <code>true</code> if a new element should be created replacing the given one on first edit of a container-page
132     **/
133    public CmsContainerElementBean(
134        CmsUUID elementId,
135        CmsUUID formatterId,
136        Map<String, String> individualSettings,
137        boolean createNew) {
138
139        m_elementId = elementId;
140        m_formatterId = formatterId;
141        Map<String, String> newSettings = (individualSettings == null
142        ? new HashMap<String, String>()
143        : new HashMap<String, String>(individualSettings));
144        if (!newSettings.containsKey(CmsContainerElement.ELEMENT_INSTANCE_ID)) {
145            newSettings.put(CmsContainerElement.ELEMENT_INSTANCE_ID, new CmsUUID().toString());
146        }
147        newSettings.values().removeAll(Collections.singletonList(null));
148        m_individualSettings = Collections.unmodifiableMap(newSettings);
149        m_editorHash = m_elementId.toString() + getSettingsHash();
150        m_createNew = createNew;
151    }
152
153    /**
154     * Constructor to enable wrapped elements.<p>
155     */
156    protected CmsContainerElementBean() {
157
158        m_elementId = null;
159        m_createNew = false;
160    }
161
162    /**
163     * Cloning constructor.<p>
164     *
165     * @param createNew create new flag
166     * @param elementId element id
167     * @param formatterId formatter id
168     * @param individualSettings individual settings
169     * @param inheritanceInfo inheritance info
170     * @param inMemoryOnly in memory only flag
171     * @param temporaryContent temporary content flag
172     * @param releasedAndNotExpired released and not expired flag
173     * @param resource the resource/file object
174     * @param settings the settings
175     * @param sitePath the site path
176     */
177    private CmsContainerElementBean(
178        boolean createNew,
179        CmsUUID elementId,
180        CmsUUID formatterId,
181        Map<String, String> individualSettings,
182        CmsInheritanceInfo inheritanceInfo,
183        boolean inMemoryOnly,
184        boolean temporaryContent,
185        boolean releasedAndNotExpired,
186        CmsResource resource,
187        Map<String, String> settings,
188        String sitePath) {
189
190        m_createNew = createNew;
191        m_elementId = elementId;
192        m_formatterId = formatterId;
193        m_individualSettings = Collections.unmodifiableMap(individualSettings);
194        m_inheritanceInfo = inheritanceInfo;
195        m_inMemoryOnly = inMemoryOnly;
196        m_releasedAndNotExpired = releasedAndNotExpired;
197        m_resource = resource;
198        setSettings(settings);
199        m_sitePath = sitePath;
200        m_temporaryContent = temporaryContent;
201    }
202
203    /**
204     * Clones the given element bean with a different formatter.<p>
205     *
206     * @param source the element to clone
207     * @param formatterId the new formatter id
208     *
209     * @return the element bean
210     */
211    public static CmsContainerElementBean cloneWithFormatter(CmsContainerElementBean source, CmsUUID formatterId) {
212
213        CmsContainerElementBean result = source.clone();
214        result.m_formatterId = formatterId;
215        return result;
216    }
217
218    /**
219     * Clones the given element bean with a different set of settings.<p>
220     *
221     * @param source the element to clone
222     * @param settings the new settings
223     *
224     * @return the element bean
225     */
226    public static CmsContainerElementBean cloneWithSettings(
227        CmsContainerElementBean source,
228        Map<String, String> settings) {
229
230        boolean createNew = source.m_createNew;
231        if (settings.containsKey(CmsContainerElement.CREATE_AS_NEW)) {
232            createNew = Boolean.valueOf(settings.get(CmsContainerElement.CREATE_AS_NEW)).booleanValue();
233            settings = new HashMap<String, String>(settings);
234            settings.remove(CmsContainerElement.CREATE_AS_NEW);
235        }
236        CmsContainerElementBean result = new CmsContainerElementBean(
237            source.m_elementId,
238            source.m_formatterId,
239            settings,
240            createNew);
241        result.m_resource = source.m_resource;
242        result.m_sitePath = source.m_sitePath;
243        result.m_inMemoryOnly = source.m_inMemoryOnly;
244        result.m_inheritanceInfo = source.m_inheritanceInfo;
245        if (result.m_inMemoryOnly) {
246            String editorHash = source.m_editorHash;
247            if (editorHash.contains(CmsADEManager.CLIENT_ID_SEPERATOR)) {
248                editorHash = editorHash.substring(0, editorHash.indexOf(CmsADEManager.CLIENT_ID_SEPERATOR));
249            }
250            editorHash += result.getSettingsHash();
251            result.m_editorHash = editorHash;
252        }
253        return result;
254    }
255
256    /**
257     * Creates an element bean for the given resource type.<p>
258     * <b>The represented resource will be in memory only and not in the VFS!!!.</b><p>
259     *
260     * @param cms the CMS context
261     * @param resourceType the resource type
262     * @param targetFolder the parent folder of the resource
263     * @param individualSettings the element settings as a map of name/value pairs
264     * @param isCopyModels if this element when used in models should be copied instead of reused
265     * @param locale the locale to use
266     *
267     * @return the created element bean
268     * @throws CmsException if something goes wrong creating the element
269     * @throws IllegalArgumentException if the resource type not instance of {@link org.opencms.file.types.CmsResourceTypeXmlContent}
270     */
271    public static CmsContainerElementBean createElementForResourceType(
272        CmsObject cms,
273        I_CmsResourceType resourceType,
274        String targetFolder,
275        Map<String, String> individualSettings,
276        boolean isCopyModels,
277        Locale locale)
278    throws CmsException {
279
280        if (!(resourceType instanceof CmsResourceTypeXmlContent)) {
281            throw new IllegalArgumentException();
282        }
283
284        byte[] content = new byte[0];
285        String schema = ((CmsResourceTypeXmlContent)resourceType).getSchema();
286        if (schema != null) {
287            // must set URI of OpenCms user context to parent folder of created resource,
288            // in order to allow reading of properties for default values
289            CmsObject newCms = OpenCms.initCmsObject(cms);
290            newCms.getRequestContext().setUri(targetFolder);
291            // unmarshal the content definition for the new resource
292            CmsXmlContentDefinition contentDefinition = CmsXmlContentDefinition.unmarshal(cms, schema);
293            CmsXmlContent xmlContent = CmsXmlContentFactory.createDocument(
294                newCms,
295                locale,
296                OpenCms.getSystemInfo().getDefaultEncoding(),
297                contentDefinition);
298            // adding all other available locales
299            for (Locale otherLocale : OpenCms.getLocaleManager().getAvailableLocales()) {
300                if (!locale.equals(otherLocale)) {
301                    xmlContent.addLocale(newCms, otherLocale);
302                }
303            }
304            content = xmlContent.marshal();
305        }
306        @SuppressWarnings("deprecation")
307        CmsFile file = new CmsFile(
308            CmsUUID.getNullUUID(),
309            CmsUUID.getNullUUID(),
310            targetFolder + "~",
311            resourceType.getTypeId(),
312            0,
313            cms.getRequestContext().getCurrentProject().getUuid(),
314            CmsResource.STATE_NEW,
315            0,
316            cms.getRequestContext().getCurrentUser().getId(),
317            0,
318            cms.getRequestContext().getCurrentUser().getId(),
319            CmsResource.DATE_RELEASED_DEFAULT,
320            CmsResource.DATE_EXPIRED_DEFAULT,
321            1,
322            content.length,
323            0,
324            0,
325            content);
326        CmsContainerElementBean elementBean = new CmsContainerElementBean(
327            file,
328            null,
329            individualSettings,
330            true,
331            resourceType.getTypeName() + getSettingsHash(individualSettings, isCopyModels),
332            isCopyModels);
333        return elementBean;
334    }
335
336    /**
337     * Gets the hash code for the element settings.<p>
338     *
339     * @param individualSettings the individual settings
340     * @param createNew the create new flag
341     *
342     * @return the hash code for the element settings
343     */
344    private static String getSettingsHash(Map<String, String> individualSettings, boolean createNew) {
345
346        if (!individualSettings.isEmpty() || createNew) {
347            int hash = (individualSettings.toString() + createNew).hashCode();
348            return CmsADEManager.CLIENT_ID_SEPERATOR + hash;
349        }
350        return "";
351    }
352
353    /**
354     * Adds a formatter setting.<p>
355     *
356     * @param containerName the container name
357     * @param formatterId the formatter id
358     */
359    public void addFormatterSetting(String containerName, String formatterId) {
360
361        Map<String, String> newSettings = new HashMap<String, String>(m_individualSettings);
362        newSettings.put(CmsFormatterConfig.getSettingsKeyForContainer(containerName), formatterId);
363        m_individualSettings = Collections.unmodifiableMap(newSettings);
364        if (m_inMemoryOnly) {
365            String editorHash = m_editorHash;
366            if (editorHash.contains(CmsADEManager.CLIENT_ID_SEPERATOR)) {
367                editorHash = editorHash.substring(0, editorHash.indexOf(CmsADEManager.CLIENT_ID_SEPERATOR));
368            }
369            editorHash += getSettingsHash();
370            m_editorHash = editorHash;
371        } else {
372            m_editorHash = m_elementId.toString() + getSettingsHash();
373        }
374    }
375
376    /**
377     * @see java.lang.Object#clone()
378     */
379    @Override
380    public CmsContainerElementBean clone() {
381
382        return new CmsContainerElementBean(
383            m_createNew,
384            m_elementId,
385            m_formatterId,
386            m_individualSettings,
387            m_inheritanceInfo,
388            m_inMemoryOnly,
389            m_temporaryContent,
390            m_releasedAndNotExpired,
391            m_resource,
392            m_settings,
393            m_sitePath);
394    }
395
396    /**
397     * Returns the ADE client editor has value.<p>
398     *
399     * @return the ADE client editor has value
400     */
401    public String editorHash() {
402
403        if (m_editorHash == null) {
404            m_editorHash = m_elementId.toString() + getSettingsHash();
405        }
406        return m_editorHash;
407    }
408
409    /**
410     * @see java.lang.Object#equals(java.lang.Object)
411     */
412    @Override
413    public boolean equals(Object obj) {
414
415        if (!(obj instanceof CmsContainerElementBean)) {
416            return false;
417        }
418        return editorHash().equals(((CmsContainerElementBean)obj).editorHash());
419    }
420
421    /**
422     * Returns the structure id of the formatter of this element.<p>
423     *
424     * @return the structure id of the formatter of this element
425     */
426    public CmsUUID getFormatterId() {
427
428        return m_formatterId;
429    }
430
431    /**
432     * Returns the structure id of the resource of this element.<p>
433     *
434     * @return the structure id of the resource of this element
435     */
436    public CmsUUID getId() {
437
438        return m_elementId;
439    }
440
441    /**
442     * Returns the settings of this element.<p>
443     *
444     * @return the settings of this element
445     */
446    public Map<String, String> getIndividualSettings() {
447
448        return m_individualSettings;
449    }
450
451    /**
452     * Returns the inheritance info.<p>
453     *
454     * @return the inheritance info or <code>null</code> if not available
455     */
456    public CmsInheritanceInfo getInheritanceInfo() {
457
458        return m_inheritanceInfo;
459    }
460
461    /**
462     * Returns the element instance id.<p>
463     *
464     * @return the element instance id
465     */
466    public String getInstanceId() {
467
468        return getIndividualSettings().get(CmsContainerElement.ELEMENT_INSTANCE_ID);
469    }
470
471    /**
472     * Returns the resource of this element.<p>
473     *
474     * It is required to call {@link #initResource(CmsObject)} before this method can be used.<p>
475     *
476     * @return the resource of this element
477     *
478     * @see #initResource(CmsObject)
479     */
480    public CmsResource getResource() {
481
482        return m_resource;
483    }
484
485    /**
486     * Returns the element settings including default values for settings not set.<p>
487     * Will return <code>null</code> if the element bean has not been initialized with {@link #initResource(org.opencms.file.CmsObject)}.<p>
488     *
489     * @return the element settings
490     */
491    public Map<String, String> getSettings() {
492
493        return m_settings;
494    }
495
496    /**
497     * Returns the site path of the resource of this element.<p>
498     *
499     * It is required to call {@link #initResource(CmsObject)} before this method can be used.<p>
500     *
501     * @return the site path of the resource of this element
502     *
503     * @see #initResource(CmsObject)
504     */
505    public String getSitePath() {
506
507        return m_sitePath;
508    }
509
510    /**
511     * Returns the resource type name.<p>
512     *
513     * @return the type name
514     */
515    public String getTypeName() {
516
517        if (getResource() != null) {
518            return OpenCms.getResourceManager().getResourceType(getResource()).getTypeName();
519        } else {
520            return "unknown";
521        }
522    }
523
524    /**
525     * @see java.lang.Object#hashCode()
526     */
527    @Override
528    public int hashCode() {
529
530        return m_editorHash.hashCode();
531    }
532
533    /**
534     * Initializes the resource and the site path of this element.<p>
535     *
536     * @param cms the CMS context
537     *
538     * @throws CmsException if something goes wrong reading the element resource
539     */
540    public void initResource(CmsObject cms) throws CmsException {
541
542        if (m_resource == null) {
543            m_resource = cms.readResource(getId(), CmsResourceFilter.IGNORE_EXPIRATION);
544            m_releasedAndNotExpired = m_resource.isReleasedAndNotExpired(cms.getRequestContext().getRequestTime());
545        } else if (!isInMemoryOnly()) {
546            CmsUUID id = m_resource.getStructureId();
547            if (id == null) {
548                id = getId();
549            }
550            // the resource object may have a wrong root path, e.g. if it was created before the resource was moved
551            if (cms.getRequestContext().getCurrentProject().isOnlineProject()) {
552                m_resource = cms.readResource(id, CmsResourceFilter.IGNORE_EXPIRATION);
553                m_releasedAndNotExpired = m_resource.isReleasedAndNotExpired(cms.getRequestContext().getRequestTime());
554            } else {
555                if (!isTemporaryContent()) {
556                    m_resource = cms.readResource(getId(), CmsResourceFilter.IGNORE_EXPIRATION);
557                }
558                m_releasedAndNotExpired = m_resource.isReleasedAndNotExpired(cms.getRequestContext().getRequestTime());
559            }
560        }
561        if (m_settings == null) {
562            setSettings(new HashMap<String, String>(getIndividualSettings()));
563        }
564        // redo on every init call to ensure sitepath is calculated for current site
565        m_sitePath = cms.getSitePath(m_resource);
566    }
567
568    /**
569     * Initializes the element settings.<p>
570     *
571     * @param cms the CMS context
572     * @param formatterBean the formatter configuration bean
573     * @param locale the content locale
574     * @param request the current request, if available
575     * @param presets the presets for container element settings
576     */
577    public void initSettings(
578        CmsObject cms,
579        I_CmsFormatterBean formatterBean,
580        Locale locale,
581        ServletRequest request,
582        Map<String, String> presets) {
583
584        Map<String, String> mergedSettings;
585        if (formatterBean == null) {
586            mergedSettings = CmsXmlContentPropertyHelper.mergeDefaults(
587                cms,
588                m_resource,
589                getIndividualSettings(),
590                locale,
591                request);
592        } else {
593            mergedSettings = CmsXmlContentPropertyHelper.mergeDefaults(
594                cms,
595                OpenCms.getADEManager().getFormatterSettings(cms, formatterBean, getResource(), locale, request),
596                getIndividualSettings());
597        }
598        if ((presets != null) && (presets.size() > 0)) {
599            mergedSettings.putAll(presets);
600        }
601        if (m_settings == null) {
602            setSettings(mergedSettings);
603        } else {
604            m_settings.putAll(mergedSettings);
605        }
606    }
607
608    /**
609     * Returns if the given element should be used as a copy model.<p>
610     *
611     * @return <code>true</code> if the given element should be used as a copy model
612     */
613    public boolean isCopyModel() {
614
615        return Boolean.valueOf(getIndividualSettings().get(CmsContainerElement.USE_AS_COPY_MODEL)).booleanValue();
616    }
617
618    /**
619     * Returns if a new element should be created replacing the given one on first edit of a container-page.<p>
620     *
621     * @return <code>true</code> if a new element should be created replacing the given one on first edit of a container-page
622     */
623    public boolean isCreateNew() {
624
625        return m_createNew;
626    }
627
628    /**
629     * Tests whether this element refers to a group container.<p>
630     *
631     * @param cms the CmsObject used for VFS operations
632     *
633     * @return <code>true</code> if the container element refers to a group container
634     *
635     * @throws CmsException if something goes wrong
636     */
637    public boolean isGroupContainer(CmsObject cms) throws CmsException {
638
639        if (m_resource == null) {
640            initResource(cms);
641        }
642        return CmsResourceTypeXmlContainerPage.GROUP_CONTAINER_TYPE_NAME.equals(
643            OpenCms.getResourceManager().getResourceType(m_resource).getTypeName());
644    }
645
646    /**
647     * Returns whether this element refers to an inherited container element.<p>
648     *
649     * @param cms the CmsObject used for VFS operations
650     *
651     * @return <code>true</code> if the container element refers to an inherited container
652     *
653     * @throws CmsException if something goes wrong
654     */
655    @SuppressWarnings("deprecation")
656    public boolean isInheritedContainer(CmsObject cms) throws CmsException {
657
658        if (m_resource == null) {
659            initResource(cms);
660        }
661        return OpenCms.getResourceManager().getResourceType(
662            CmsResourceTypeXmlContainerPage.INHERIT_CONTAINER_TYPE_NAME).getTypeId() == m_resource.getTypeId();
663    }
664
665    /**
666     * Returns if the represented resource is in memory only and not persisted in the VFS.<p>
667     *
668     * @return <code>true</code> if the represented resource is in memory only and not persisted in the VFS
669     */
670    public boolean isInMemoryOnly() {
671
672        return m_inMemoryOnly;
673    }
674
675    /**
676     * Returns if the given element is a model group.<p>
677     *
678     * @return <code>true</code> if the given element is a model group
679     */
680    public boolean isModelGroup() {
681
682        ModelGroupState state = ModelGroupState.evaluate(
683            getIndividualSettings().get(CmsContainerElement.MODEL_GROUP_STATE));
684        return state == ModelGroupState.isModelGroup;
685    }
686
687    /**
688     * Returns if all instances of this element should be replaced within a copy model.<p>
689     *
690     * @return <code>true</code> if all instances of this element should be replaced within a copy model
691     */
692    public boolean isModelGroupAlwaysReplace() {
693
694        return Boolean.parseBoolean(getIndividualSettings().get(CmsContainerElement.IS_MODEL_GROUP_ALWAYS_REPLACE));
695    }
696
697    /**
698     * Returns if the element resource is released and not expired.<p>
699     *
700     * @return <code>true</code> if the element resource is released and not expired
701     */
702    public boolean isReleasedAndNotExpired() {
703
704        return isInMemoryOnly() || m_releasedAndNotExpired;
705    }
706
707    /**
708     * Returns if the element resource contains temporary file content.<p>
709     *
710     * @return <code>true</code> if the element resource contains temporary file content
711     */
712    public boolean isTemporaryContent() {
713
714        return m_temporaryContent;
715    }
716
717    /**
718     * Removes the instance id.<p>
719     */
720    public void removeInstanceId() {
721
722        Map<String, String> newSettings = new HashMap<String, String>(m_individualSettings);
723        newSettings.remove(CmsContainerElement.ELEMENT_INSTANCE_ID);
724        m_individualSettings = Collections.unmodifiableMap(newSettings);
725        m_editorHash = m_elementId.toString() + getSettingsHash();
726    }
727
728    /**
729     * Sets the formatter id.<p>
730     *
731     * @param formatterId the formatter id
732     */
733    public void setFormatterId(CmsUUID formatterId) {
734
735        m_formatterId = formatterId;
736    }
737
738    /**
739     * Sets a historical file.<p>
740     *
741     * @param file the historical file
742     */
743    public void setHistoryFile(CmsFile file) {
744
745        m_resource = file;
746        m_inMemoryOnly = true;
747    }
748
749    /**
750     * Sets the inheritance info for this element.<p>
751     *
752     * @param inheritanceInfo the inheritance info
753     */
754    public void setInheritanceInfo(CmsInheritanceInfo inheritanceInfo) {
755
756        m_inheritanceInfo = inheritanceInfo;
757    }
758
759    /**
760     * Sets the element resource as a temporary file.<p>
761     *
762     * @param elementFile the temporary file
763     */
764    public void setTemporaryFile(CmsFile elementFile) {
765
766        m_resource = elementFile;
767        m_temporaryContent = true;
768    }
769
770    /**
771     * @see java.lang.Object#toString()
772     */
773    @Override
774    public String toString() {
775
776        return editorHash();
777    }
778
779    /**
780     * Updates the individual settings.<p>
781     *
782     * This causes all merged settings (from defaults etc.) to be lost.
783     *
784     * @param newSettings the new settings
785     */
786    public void updateIndividualSettings(Map<String, String> newSettings) {
787
788        m_individualSettings = Collections.unmodifiableMap(newSettings);
789        setSettings(getIndividualSettings());
790    }
791
792    /**
793     * Gets the hash code for the element settings.<p>
794     *
795     * @return the hash code for the element settings
796     */
797    private String getSettingsHash() {
798
799        String instanceId = getInstanceId();
800        if (instanceId == null) {
801            throw new RuntimeException("Missing instance id");
802        }
803        return CmsADEManager.CLIENT_ID_SEPERATOR + getInstanceId();
804    }
805
806    /**
807     * Sets the settings map.<p>
808     *
809     * @param settings the settings
810     */
811    private void setSettings(Map<String, String> settings) {
812
813        if (settings == null) {
814            m_settings = null;
815        } else {
816            m_settings = new CmsNullIgnoringConcurrentMap<String, String>(settings);
817        }
818    }
819}