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}