001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://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: https://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: https://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
091        m_primaryResource = primaryResource;
092        m_secondaryResources = Sets.newHashSet(secondaryResources);
093        m_cms = cms;
094        initLocales();
095    }
096
097    /**
098     * Gets the list of all resources of this group (primary and secondary).<p>
099     *
100     * @return the list of all resources of this group
101     */
102    public List<CmsResource> getAllResources() {
103
104        List<CmsResource> result = Lists.newArrayList();
105        result.add(m_primaryResource);
106        for (CmsResource res : getSecondaryResources()) {
107            result.add(res);
108        }
109        return result;
110    }
111
112    /**
113     * Gets the main locale (i.e. the locale of the primary resource of this group).<p>
114     *
115     * @return the main locale
116     */
117    public Locale getMainLocale() {
118
119        return m_localeCache.get(m_primaryResource);
120    }
121
122    /**
123     * Gets the primary resource.<p>
124     *
125     * @return the primary resource
126     */
127    public CmsResource getPrimaryResource() {
128
129        return m_primaryResource;
130    }
131
132    /**
133     * Gets a map which contains the resources of the locale group as keys, indexed by their locale.<p>
134     *
135     * If the locale group contains more than one resource from the same locale,, which one is used a map value is undefined.
136     *
137     * @return the map of resources by locale
138     */
139    public Map<Locale, CmsResource> getResourcesByLocale() {
140
141        List<CmsResource> resources = Lists.newArrayList();
142        resources.add(m_primaryResource);
143        resources.addAll(m_secondaryResources);
144        Collections.sort(resources, new Comparator<CmsResource>() {
145
146            public int compare(CmsResource arg0, CmsResource arg1) {
147
148                String path1 = arg0.getRootPath();
149                String path2 = arg1.getRootPath();
150                return path2.compareTo(path1);
151            }
152
153        });
154        Map<Locale, CmsResource> result = new LinkedHashMap<Locale, CmsResource>();
155        for (CmsResource resource : resources) {
156            result.put(m_localeCache.get(resource), resource);
157        }
158        return result;
159
160    }
161
162    /**
163     * Gets the resources of this group which have the given locale.<p>
164     *
165     * @param locale a locale
166     * @return the collection of resources with the given locale
167     */
168    public Collection<CmsResource> getResourcesForLocale(Locale locale) {
169
170        return Lists.newArrayList(m_resourcesByLocale.get(locale));
171    }
172
173    /**
174     * Gets the secondary resources of this group.<p>
175     *
176     * @return the collection of secondary resources
177     */
178    public Set<CmsResource> getSecondaryResources() {
179
180        return Collections.unmodifiableSet(m_secondaryResources);
181    }
182
183    /**
184     * Checks if this group has a resource with the given locale.<p>
185     *
186     * @param locale the locale
187     * @return true  if the group has a resource with the locale
188     */
189    public boolean hasLocale(Locale locale) {
190
191        return m_resourcesByLocale.containsKey(locale);
192    }
193
194    /**
195     * Checks if the locale group is marked as not translatable for the given locale.<p>
196     *
197     * @param locale a locale
198     *
199     * @return true if the locale group is marked as not translatable for the given locale
200     */
201    public boolean isMarkedNoTranslation(Locale locale) {
202
203        return (m_noTranslation != null) && CmsLocaleManager.getLocales(m_noTranslation).contains(locale);
204    }
205
206    /**
207     * Checks if the locale group is marked as not translatable for any of the given locales.<p>
208     *
209     * @param locales a set of locales
210     * @return true if the locale group is marked as not translatable for any of the  given resources
211     */
212    public boolean isMarkedNoTranslation(Set<Locale> locales) {
213
214        if (m_noTranslation == null) {
215            return false;
216        }
217        List<Locale> noTranslationLocales = CmsLocaleManager.getLocales(m_noTranslation);
218        for (Locale locale : noTranslationLocales) {
219            if (locales.contains(locale)) {
220                return true;
221            }
222        }
223        return false;
224    }
225
226    /**
227     * 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
228     * in which it is located.<p>
229     *
230     * @return true if this is a potential group head
231     */
232    public boolean isPotentialGroupHead() {
233
234        CmsSite site = OpenCms.getSiteManager().getSiteForRootPath(m_primaryResource.getRootPath());
235        if (site == null) {
236            return false;
237        }
238        Locale mainLocale = site.getMainTranslationLocale(null);
239        if (mainLocale == null) {
240            return false;
241        }
242        Locale primaryLocale = getMainLocale();
243        return mainLocale.equals(primaryLocale);
244
245    }
246
247    /**
248     * Checks if this group is a real group, i.e. consists of more than one resource.<p>
249     *
250     * @return true if this is a real group
251     */
252    public boolean isRealGroup() {
253
254        return m_secondaryResources.size() > 0;
255    }
256
257    /**
258     * Checks if this is either a real group or a potential group head (i.e. a potential primary resource).<p>
259     *
260     * @return true if this is a real group or a potential group head
261     */
262    public boolean isRealGroupOrPotentialGroupHead() {
263
264        return isRealGroup() || isPotentialGroupHead();
265    }
266
267    /**
268     * Gets the locales of the resources from  this locale group.<p>
269     *
270     * @return the locales of this locale group
271     */
272    Set<Locale> getLocales() {
273
274        return Sets.newHashSet(getResourcesByLocale().keySet());
275    }
276
277    /**
278     * Initializes the locales.<p>
279     *
280     */
281    private void initLocales() {
282
283        if (!m_localeCache.isEmpty()) {
284            return;
285        }
286        readLocale(m_primaryResource);
287        for (CmsResource resource : m_secondaryResources) {
288            readLocale(resource);
289        }
290        for (Map.Entry<CmsResource, Locale> entry : m_localeCache.entrySet()) {
291            CmsResource key = entry.getKey();
292            Locale value = entry.getValue();
293            m_resourcesByLocale.put(value, key);
294        }
295        try {
296            CmsProperty noTranslationProp = m_cms.readPropertyObject(
297                m_primaryResource,
298                CmsPropertyDefinition.PROPERTY_LOCALE_NOTRANSLATION,
299                false);
300            m_noTranslation = noTranslationProp.getValue();
301        } catch (CmsException e) {
302            LOG.error(e.getLocalizedMessage(), e);
303
304        }
305    }
306
307    /**
308     * Reads the locale for the given resource.<p>
309     *
310     * @param res the locale for the resource
311     */
312    private void readLocale(CmsResource res) {
313
314        Locale locale = OpenCms.getLocaleManager().getDefaultLocale(m_cms, res);
315        m_localeCache.put(res, locale);
316    }
317
318}