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.i18n;
029
030import org.opencms.file.CmsObject;
031import org.opencms.file.CmsProperty;
032import org.opencms.file.CmsPropertyDefinition;
033import org.opencms.file.CmsResource;
034import org.opencms.main.CmsException;
035import org.opencms.main.CmsLog;
036import org.opencms.main.OpenCms;
037import org.opencms.site.CmsSite;
038
039import java.util.Collection;
040import java.util.Collections;
041import java.util.Comparator;
042import java.util.LinkedHashMap;
043import java.util.List;
044import java.util.Locale;
045import java.util.Map;
046import java.util.Set;
047
048import org.apache.commons.logging.Log;
049
050import com.google.common.collect.ArrayListMultimap;
051import com.google.common.collect.Lists;
052import com.google.common.collect.Maps;
053import com.google.common.collect.Multimap;
054import com.google.common.collect.Sets;
055
056/**
057 * Represents a group of resources which are locale variants of each other.<p>
058 */
059public class CmsLocaleGroup {
060
061    /** The logger instance for this class. */
062    private static final Log LOG = CmsLog.getLog(CmsLocaleGroup.class);
063
064    /** The CMS context to use. */
065    private CmsObject m_cms;
066
067    /** The locale cache. */
068    private Map<CmsResource, Locale> m_localeCache = Maps.newHashMap();
069
070    /** The 'no translation' setting for this locale group. */
071    private String m_noTranslation;
072
073    /** The primary resource. */
074    private CmsResource m_primaryResource;
075
076    /** Map of resources by locale. */
077    private Multimap<Locale, CmsResource> m_resourcesByLocale = ArrayListMultimap.create();
078
079    /** The secondary resources. */
080    private Set<CmsResource> m_secondaryResources;
081
082    /**
083     * Creates a new instance.<p>
084     *
085     * @param cms the CMS context to use
086     * @param primaryResource the primary resource
087     * @param secondaryResources the secondary resources
088     */
089    public CmsLocaleGroup(CmsObject cms, CmsResource primaryResource, List<CmsResource> secondaryResources) {
090        m_primaryResource = primaryResource;
091        m_secondaryResources = Sets.newHashSet(secondaryResources);
092        m_cms = cms;
093        initLocales();
094    }
095
096    /**
097     * Gets the list of all resources of this group (primary and secondary).<p>
098     *
099     * @return the list of all resources of this group
100     */
101    public List<CmsResource> getAllResources() {
102
103        List<CmsResource> result = Lists.newArrayList();
104        result.add(m_primaryResource);
105        for (CmsResource res : getSecondaryResources()) {
106            result.add(res);
107        }
108        return result;
109    }
110
111    /**
112     * Gets the main locale (i.e. the locale of the primary resource of this group).<p>
113     *
114     * @return the main locale
115     */
116    public Locale getMainLocale() {
117
118        return m_localeCache.get(m_primaryResource);
119    }
120
121    /**
122     * Gets the primary resource.<p>
123     *
124     * @return the primary resource
125     */
126    public CmsResource getPrimaryResource() {
127
128        return m_primaryResource;
129    }
130
131    /**
132     * Gets a map which contains the resources of the locale group as keys, indexed by their locale.<p>
133     *
134     * If the locale group contains more than one resource from the same locale,, which one is used a map value is undefined.
135     *
136     * @return the map of resources by locale
137     */
138    public Map<Locale, CmsResource> getResourcesByLocale() {
139
140        List<CmsResource> resources = Lists.newArrayList();
141        resources.add(m_primaryResource);
142        resources.addAll(m_secondaryResources);
143        Collections.sort(resources, new Comparator<CmsResource>() {
144
145            public int compare(CmsResource arg0, CmsResource arg1) {
146
147                String path1 = arg0.getRootPath();
148                String path2 = arg1.getRootPath();
149                return path2.compareTo(path1);
150            }
151
152        });
153        Map<Locale, CmsResource> result = new LinkedHashMap<Locale, CmsResource>();
154        for (CmsResource resource : resources) {
155            result.put(m_localeCache.get(resource), resource);
156        }
157        return result;
158
159    }
160
161    /**
162     * Gets the resources of this group which have the given locale.<p>
163     *
164     * @param locale a locale
165     * @return the collection of resources with the given locale
166     */
167    public Collection<CmsResource> getResourcesForLocale(Locale locale) {
168
169        return Lists.newArrayList(m_resourcesByLocale.get(locale));
170    }
171
172    /**
173     * Gets the secondary resources of this group.<p>
174     *
175     * @return the collection of secondary resources
176     */
177    public Set<CmsResource> getSecondaryResources() {
178
179        return Collections.unmodifiableSet(m_secondaryResources);
180    }
181
182    /**
183     * Checks if this group has a resource with the given locale.<p>
184     *
185     * @param locale the locale
186     * @return true  if the group has a resource with the locale
187     */
188    public boolean hasLocale(Locale locale) {
189
190        return m_resourcesByLocale.containsKey(locale);
191    }
192
193    /**
194     * Checks if the locale group is marked as not translatable for the given locale.<p>
195     *
196     * @param locale a locale
197     *
198     * @return true if the locale group is marked as not translatable for the given locale
199     */
200    public boolean isMarkedNoTranslation(Locale locale) {
201
202        return (m_noTranslation != null) && CmsLocaleManager.getLocales(m_noTranslation).contains(locale);
203    }
204
205    /**
206     * Checks if the locale group is marked as not translatable for any of the given locales.<p>
207     *
208     * @param locales a set of locales
209     * @return true if the locale group is marked as not translatable for any of the  given resources
210     */
211    public boolean isMarkedNoTranslation(Set<Locale> locales) {
212
213        if (m_noTranslation == null) {
214            return false;
215        }
216        List<Locale> noTranslationLocales = CmsLocaleManager.getLocales(m_noTranslation);
217        for (Locale locale : noTranslationLocales) {
218            if (locales.contains(locale)) {
219                return true;
220            }
221        }
222        return false;
223    }
224
225    /**
226     * Returns true if this is a potential group head, i.e. the locale of the primary resource is the main translation locale configured for the site
227     * in which it is located.<p>
228     *
229     * @return true if this is a potential group head
230     */
231    public boolean isPotentialGroupHead() {
232
233        CmsSite site = OpenCms.getSiteManager().getSiteForRootPath(m_primaryResource.getRootPath());
234        if (site == null) {
235            return false;
236        }
237        Locale mainLocale = site.getMainTranslationLocale(null);
238        if (mainLocale == null) {
239            return false;
240        }
241        Locale primaryLocale = getMainLocale();
242        return mainLocale.equals(primaryLocale);
243
244    }
245
246    /**
247     * Checks if this group is a real group, i.e. consists of more than one resource.<p>
248     *
249     * @return true if this is a real group
250     */
251    public boolean isRealGroup() {
252
253        return m_secondaryResources.size() > 0;
254    }
255
256    /**
257     * Checks if this is either a real group or a potential group head (i.e. a potential primary resource).<p>
258     *
259     * @return true if this is a real group or a potential group head
260     */
261    public boolean isRealGroupOrPotentialGroupHead() {
262
263        return isRealGroup() || isPotentialGroupHead();
264    }
265
266    /**
267     * Gets the locales of the resources from  this locale group.<p>
268     *
269     * @return the locales of this locale group
270     */
271    Set<Locale> getLocales() {
272
273        return Sets.newHashSet(getResourcesByLocale().keySet());
274    }
275
276    /**
277     * Initializes the locales.<p>
278     *
279     */
280    private void initLocales() {
281
282        if (!m_localeCache.isEmpty()) {
283            return;
284        }
285        readLocale(m_primaryResource);
286        for (CmsResource resource : m_secondaryResources) {
287            readLocale(resource);
288        }
289        for (Map.Entry<CmsResource, Locale> entry : m_localeCache.entrySet()) {
290            CmsResource key = entry.getKey();
291            Locale value = entry.getValue();
292            m_resourcesByLocale.put(value, key);
293        }
294        try {
295            CmsProperty noTranslationProp = m_cms.readPropertyObject(
296                m_primaryResource,
297                CmsPropertyDefinition.PROPERTY_LOCALE_NOTRANSLATION,
298                false);
299            m_noTranslation = noTranslationProp.getValue();
300        } catch (CmsException e) {
301            LOG.error(e.getLocalizedMessage(), e);
302
303        }
304    }
305
306    /**
307     * Reads the locale for the given resource.<p>
308     *
309     * @param res the locale for the resource
310     */
311    private void readLocale(CmsResource res) {
312
313        Locale locale = OpenCms.getLocaleManager().getDefaultLocale(m_cms, res);
314        m_localeCache.put(res, locale);
315    }
316
317}