/*
* Weblounge: Web Content Management System
* Copyright (c) 2003 - 2011 The Weblounge Team
* http://entwinemedia.com/weblounge
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package ch.entwine.weblounge.common.impl.language;
import ch.entwine.weblounge.common.language.Language;
import ch.entwine.weblounge.common.language.LocalizationListener;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
/**
* This class represents a general object container that is capable of
* presenting the same content in various languages.
* <p>
* To be able to properly handle this object it should be noted that every
* localizable content will have an <i>original</i> language, a
* <code>current language</code> and possibly also a <i>default language</i>.
* <ul>
* <li><b>Original language</b>: the original language is automatically set as
* soon as localized content is added and thus represents the language of that
* first content.</li>
* <li><b>Current language</b>: the current language is what the object has been
* set to using the <code>switchTo(Language)</code> method. If that method has
* never been called, then the current language is either the original language
* or the default one if it has been set. In any case, as long as there is
* <i>some</code> content in the object, the current language will always be
* part of what is returned by <code>getSupportedLanguages()</code>, i. e.
* <code>supportsLanguage(currentLanguage)</code> will be true as long as
* <code>getCurrentLanguage()</code> does not return <code>null</code>.</li>
* <li><b>Default language</b>: The default language can be specified on a
* localized object as needed and yields an alternative way of getting content
* that is not available in the requested language.</li>
* </ul>
*/
public class LocalizableContent<T> extends LocalizableObject implements LocalizationListener, Cloneable {
/** the content in various languages */
protected Map<Language, T> content = null;
/**
* Constructor for a localizable object with a default behavior of
* {@link LanguageResolution#Original}.
*/
public LocalizableContent() {
content = new HashMap<Language, T>();
}
/**
* Creates localizable content with a default language <code>language</code>
* and the behavior set to {@link LanguageResolution#Default} as long as
* <code>language</code> is not set to <code>null</code>, in which case the
* behavior will be set to {@link LanguageResolution#Original}.
*
* @param language
* the default language
*/
public LocalizableContent(Language language) {
super(language);
content = new HashMap<Language, T>();
}
/**
* Creates localizable content that registers with the given
* <code>localizable</code> to get notified in case of language switches. The
* <code>localizable</code> will then be called using
* {@link LocalizationListener#switchedTo(Language)}.
*
* @param localizable
* the parent localizable
*/
public LocalizableContent(LocalizableObject localizable) {
this(localizable.getDefaultLanguage());
if (localizable.getOriginalLanguage() != null)
this.setOriginalLanguage(localizable.getOriginalLanguage());
if (localizable.getDefaultLanguage() != null)
this.setDefaultLanguage(localizable.getDefaultLanguage());
this.setLanguageResolution(localizable.getLanguageResolution());
localizable.addLocalizationListener(this);
}
/**
* Removes all content from the object, and only the language resolution is
* kept as is.
*/
public void clear() {
content.clear();
languages.clear();
}
/**
* Returns <code>true</code> if there is no content at all stored in the
* object.
*
* @return <code>true</code> if the object is empty
*/
public boolean isEmpty() {
return content.isEmpty();
}
/**
* Returns the number of localized versions of the content by looking at the
* number of supported languages.
*
* @return the number of localized versions
*/
public int size() {
return content.size();
}
/**
* Returns the content in all languages.
*
* @return the content
*/
public Collection<T> values() {
return content.values();
}
/**
* Removes the content as well as support for this language from this object.
*
* @see ch.entwine.weblounge.common.impl.language.LocalizableObject#disableLanguage(ch.entwine.weblounge.common.language.Language)
*/
@Override
public void disableLanguage(Language language) {
if (language == null)
throw new IllegalArgumentException("Language must not be null");
super.disableLanguage(language);
content.remove(language);
}
/**
* Adds the content in the given language. Any content that might already be
* present for the given language will be dropped and returned by this method.
*
* @param content
* the content
* @param language
* the language
*/
public T put(T content, Language language) {
if (content == null)
throw new IllegalArgumentException("Content must not be null");
if (language == null)
throw new IllegalArgumentException("Language must not be null");
if (this.content.size() == 0)
this.originalLanguage = language;
enableLanguage(language);
return this.content.put(language, content);
}
/**
* Returns the content in the current language or fallback content as defined
* by the language resolution strategy.
*
* @return the content
* @see #getLanguage()
* @see #getLanguageResolution()
*/
public T get() {
Language language = getLanguage();
if (language == null)
return null;
return get(getLanguage(), false);
}
/**
* Returns the content in the specified language or fallback content as
* defined by the language resolution strategy.
*
* @param language
* the content language
* @return the content
*/
public T get(Language language) {
return get(language, false);
}
/**
* Returns the content in the specified language or fallback content as
* defined by the language resolution strategy as long as <code>force</code>
* is set to <code>true</code>.
*
* @param language
* the content language
* @param force
* <code>true</code> to force the language
* @return the content
*/
public T get(Language language, boolean force) {
if (language == null)
throw new IllegalArgumentException("Language must not be null");
T c = content.get(language);
if (c == null && !force) {
Language l = null;
switch (behavior) {
case Default:
l = getDefaultLanguage();
break;
case Original:
l = getOriginalLanguage();
break;
default:
throw new IllegalStateException(this + " is neither using default nor original language");
}
c = content.get(l);
}
return c;
}
/**
* This implementation of the {@link LocalizationListener} switches
* <code>this</code> to the same language, i. e.
* <code>this.switchTo(language)</code> is issued as a reaction to this
* callback.
*
* @see ch.entwine.weblounge.common.language.LocalizationListener#switchedTo(ch.entwine.weblounge.common.language.Language,
* ch.entwine.weblounge.common.language.Language)
*/
public void switchedTo(Language language, Language requestedLanguage) {
if (language == null)
return;
switchTo(language);
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#clone()
*/
@SuppressWarnings("unchecked")
public Object clone() throws CloneNotSupportedException {
LocalizableContent<T> c = (LocalizableContent<T>) super.clone();
c.behavior = behavior;
c.currentLanguage = currentLanguage;
c.defaultLanguage = defaultLanguage;
c.originalLanguage = originalLanguage;
// languages
c.languages = new HashSet<Language>();
c.languages.addAll(languages);
// content
c.content = new HashMap<Language, T>();
c.content.putAll(content);
return c;
}
/**
* Returns a <code>String</code> representation of the content in the
* specified language or fallback content as defined by the language
* resolution strategy as long as <code>force</code> is set to
* <code>true</code>.
*
* @param language
* the language
* @param force
* <code>true</code> to force the language
* @return the object's string representation in the given language
*/
public String toString(Language language, boolean force) {
if (language == null)
throw new IllegalArgumentException("Language must not be null");
if (content.size() == 0)
return null;
T c = content.get(language);
// Not found? Try the fall back language
if (c == null && !force)
c = content.get(resolveLanguage());
return (c != null) ? c.toString() : null;
}
}