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.file.CmsObject;
031import org.opencms.file.CmsResource;
032import org.opencms.file.CmsResourceFilter;
033import org.opencms.file.CmsVfsResourceNotFoundException;
034import org.opencms.main.CmsException;
035import org.opencms.util.CmsUUID;
036
037import java.util.Comparator;
038
039import com.google.common.base.Objects;
040
041/**
042 * A relation between two opencms resources.<p>
043 *
044 * @since 6.3.0
045 */
046public class CmsRelation {
047
048    /**
049     * A comparator for the source & target path plus the relation type of 2 relations.<p>
050     */
051    public static final Comparator<CmsRelation> COMPARATOR = new Comparator<CmsRelation>() {
052
053        /**
054         * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
055         */
056        public int compare(CmsRelation r1, CmsRelation r2) {
057
058            if (r1 == r2) {
059                return 0;
060            }
061            String p1 = r1.getSourcePath() + r1.getTargetPath() + r1.getType().getId();
062            String p2 = r2.getSourcePath() + r2.getTargetPath() + r2.getType().getId();
063
064            return p1.compareTo(p2);
065        }
066    };
067
068    /** Default value for undefined Strings. */
069    private static final String UNDEF = "";
070
071    /** Cached hash code. */
072    private int m_hashCode;
073
074    /** The structure id of the source resource. */
075    private final CmsUUID m_sourceId;
076
077    /** The path of the source resource. */
078    private final String m_sourcePath;
079
080    /** The structure id of the target resource. */
081    private final CmsUUID m_targetId;
082
083    /** The path of the target resource. */
084    private final String m_targetPath;
085
086    /** The relation type. */
087    private final CmsRelationType m_type;
088
089    /**
090     * Creates a new relation object of the given type between the given resources.<p>
091     *
092     * @param source the source resource
093     * @param target the target resource
094     * @param type the relation type
095     */
096    public CmsRelation(CmsResource source, CmsResource target, CmsRelationType type) {
097
098        this(source.getStructureId(), source.getRootPath(), target.getStructureId(), target.getRootPath(), type);
099    }
100
101    /**
102     * Base constructor.<p>
103     *
104     * @param sourceId the source structure id
105     * @param sourcePath the source path
106     * @param targetId the target structure id
107     * @param targetPath the target path
108     * @param type the relation type
109     */
110    public CmsRelation(CmsUUID sourceId, String sourcePath, CmsUUID targetId, String targetPath, CmsRelationType type) {
111
112        // make sure no value can ever be null
113        m_sourceId = ((sourceId != null) ? sourceId : CmsUUID.getNullUUID());
114        m_sourcePath = ((sourcePath != null) ? sourcePath : UNDEF);
115        m_targetId = ((targetId != null) ? targetId : CmsUUID.getNullUUID());
116        m_targetPath = ((targetPath != null) ? targetPath : UNDEF);
117        m_type = ((type != null) ? type : CmsRelationType.XML_WEAK);
118    }
119
120    /**
121     * @see java.lang.Object#equals(java.lang.Object)
122     */
123    @Override
124    public boolean equals(Object obj) {
125
126        if (this == obj) {
127            return true;
128        }
129        if (obj instanceof CmsRelation) {
130            CmsRelation other = (CmsRelation)obj;
131            return (m_type == other.m_type)
132                && (Objects.equal(m_sourcePath, other.m_sourcePath) || Objects.equal(m_sourceId, other.m_sourceId))
133                && (Objects.equal(m_targetPath, other.m_targetPath) || Objects.equal(m_targetId, other.m_targetId));
134        }
135        return false;
136    }
137
138    /**
139     * Returns the source resource when possible to read with the given filter.<p>
140     *
141     * @param cms the current user context
142     * @param filter the filter to use
143     *
144     * @return the source resource
145     *
146     * @throws CmsException if something goes wrong
147     */
148    public CmsResource getSource(CmsObject cms, CmsResourceFilter filter) throws CmsException {
149
150        try {
151            // first look up by id
152            return cms.readResource(getSourceId(), filter);
153        } catch (CmsVfsResourceNotFoundException e) {
154            // then look up by name, but from the root site
155            String storedSiteRoot = cms.getRequestContext().getSiteRoot();
156            try {
157                cms.getRequestContext().setSiteRoot("");
158                return cms.readResource(getSourcePath(), filter);
159            } finally {
160                cms.getRequestContext().setSiteRoot(storedSiteRoot);
161            }
162        }
163    }
164
165    /**
166     * Returns the structure id of the source resource.<p>
167     *
168     * @return the structure id of the source resource
169     */
170    public CmsUUID getSourceId() {
171
172        return m_sourceId;
173    }
174
175    /**
176     * Returns the path of the source resource.<p>
177     *
178     * @return the path of the source resource
179     */
180    public String getSourcePath() {
181
182        return m_sourcePath;
183    }
184
185    /**
186     * Returns the target resource when possible to read with the given filter.<p>
187     *
188     * @param cms the current user context
189     * @param filter the filter to use
190     *
191     * @return the target resource
192     *
193     * @throws CmsException if something goes wrong
194     */
195    public CmsResource getTarget(CmsObject cms, CmsResourceFilter filter) throws CmsException {
196
197        try {
198            // first look up by id
199            return cms.readResource(getTargetId(), filter);
200        } catch (CmsVfsResourceNotFoundException e) {
201            // then look up by name, but from the root site
202            String storedSiteRoot = cms.getRequestContext().getSiteRoot();
203            try {
204                cms.getRequestContext().setSiteRoot("");
205                return cms.readResource(getTargetPath(), filter);
206            } finally {
207                cms.getRequestContext().setSiteRoot(storedSiteRoot);
208            }
209        }
210    }
211
212    /**
213     * Returns the structure id of the target resource.<p>
214     *
215     * @return the structure id of the target resource
216     */
217    public CmsUUID getTargetId() {
218
219        return m_targetId;
220    }
221
222    /**
223     * Returns the path of the target resource.<p>
224     *
225     * @return the path of the target resource
226     */
227    public String getTargetPath() {
228
229        return m_targetPath;
230    }
231
232    /**
233     * Returns the relation type.<p>
234     *
235     * @return the relation type
236     */
237    public CmsRelationType getType() {
238
239        return m_type;
240    }
241
242    /**
243     * @see java.lang.Object#hashCode()
244     */
245    @Override
246    public int hashCode() {
247
248        if (m_hashCode == 0) {
249            // calculate hash code only once
250            final int PRIME = 31;
251            int result = 1;
252            result = (PRIME * result) + ((m_sourceId == null) ? 0 : m_sourceId.hashCode());
253            result = (PRIME * result) + ((m_sourcePath == null) ? 0 : m_sourcePath.hashCode());
254            result = (PRIME * result) + ((m_targetId == null) ? 0 : m_targetId.hashCode());
255            result = (PRIME * result) + ((m_targetPath == null) ? 0 : m_targetPath.hashCode());
256            result = (PRIME * result) + ((m_type == null) ? 0 : m_type.hashCode());
257            m_hashCode = result;
258        }
259        return m_hashCode;
260    }
261
262    /**
263     * @see java.lang.Object#toString()
264     */
265    @Override
266    public String toString() {
267
268        StringBuffer str = new StringBuffer();
269        str.append("CmsRelation [");
270        str.append("source id: ").append(m_sourceId).append(", ");
271        str.append("source path: ").append(m_sourcePath).append(", ");
272        str.append("target id: ").append(m_targetId).append(", ");
273        str.append("target path: ").append(m_targetPath).append(", ");
274        str.append("type: ").append(m_type);
275        str.append("]");
276        return str.toString();
277    }
278
279    /**
280     * Copies this relation, but sets the target id in the copy to the given value.
281     *
282     * @param id the new target id for the copy
283     * @return the copy with the target id
284     */
285    public CmsRelation withTargetId(CmsUUID id) {
286
287        return new CmsRelation(m_sourceId, m_sourcePath, id, m_targetPath, m_type);
288    }
289}