d:date
true
diff --git a/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java b/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java
new file mode 100644
index 0000000000..f4a48bb651
--- /dev/null
+++ b/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2006 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.node;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.alfresco.service.cmr.dictionary.DictionaryService;
+import org.alfresco.service.cmr.repository.MLText;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.namespace.QName;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Interceptor to filter out multilingual text properties from getter methods and
+ * transform to multilingual text for setter methods.
+ *
+ * This interceptor ensures that all multilingual (ML) text is transformed to the
+ * locale chosen {@link org.alfresco.service.cmr.repository.MLText#getContextLocale() for the request}
+ * for getters and transformed to the default locale type for setters.
+ *
+ * Where {@link org.alfresco.service.cmr.repository.MLText ML text} has been passed in, this
+ * will be allowed to pass.
+ *
+ * @see org.alfresco.service.cmr.repository.MLText#getContextLocale()
+ * @see org.alfresco.service.cmr.repository.NodeService#getProperty(NodeRef, QName)
+ * @see org.alfresco.service.cmr.repository.NodeService#getProperties(NodeRef)
+ * @see org.alfresco.service.cmr.repository.NodeService#setProperty(NodeRef, QName, Serializable)
+ * @see org.alfresco.service.cmr.repository.NodeService#setProperties(NodeRef, Map)
+ *
+ * @author Derek Hulley
+ */
+public class MLPropertyInterceptor implements MethodInterceptor
+{
+ private static Log logger = LogFactory.getLog(MLPropertyInterceptor.class);
+
+ private DictionaryService dictionaryService;
+
+ public void setDictionaryService(DictionaryService dictionaryService)
+ {
+ this.dictionaryService = dictionaryService;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Object invoke(MethodInvocation invocation) throws Throwable
+ {
+ Object ret = null;
+ String methodName = invocation.getMethod().getName();
+ if (methodName.equals("getProperty"))
+ {
+ ret = invocation.proceed();
+ // The return value might need to be converted to a String
+ if (ret != null && ret instanceof MLText)
+ {
+ MLText mlText = (MLText) ret;
+ ret = mlText.getDefaultValue();
+ // done
+ if (logger.isDebugEnabled())
+ {
+ logger.debug(
+ "Converted ML text: \n" +
+ " initial: " + mlText + "\n" +
+ " converted: " + ret);
+ }
+ }
+ }
+ else if (methodName.equals("getProperties"))
+ {
+ Map properties = (Map) invocation.proceed();
+ Map convertedProperties = new HashMap(properties.size() * 2);
+ // Check each return value type
+ for (Map.Entry entry : properties.entrySet())
+ {
+ QName key = entry.getKey();
+ Serializable value = entry.getValue();
+ if (value != null && value instanceof MLText)
+ {
+ MLText mlText = (MLText) value;
+ value = mlText.getDefaultValue();
+ // Store the converted value
+ convertedProperties.put(key, value);
+ }
+ else
+ {
+ // The value goes straight back in
+ convertedProperties.put(key, value);
+ }
+ }
+ ret = convertedProperties;
+ // done
+ if (logger.isDebugEnabled())
+ {
+ logger.debug(
+ "Converted getProperties return value: \n" +
+ " initial: " + properties + "\n" +
+ " converted: " + convertedProperties);
+ }
+ }
+ else
+ {
+ ret = invocation.proceed();
+ }
+ // done
+ return ret;
+ }
+}
diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImplTest.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImplTest.java
index d99ddb00fe..139281fb63 100644
--- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImplTest.java
+++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImplTest.java
@@ -20,6 +20,7 @@ import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import javax.transaction.UserTransaction;
@@ -35,6 +36,7 @@ import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
+import org.alfresco.service.cmr.repository.MLText;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
@@ -51,6 +53,7 @@ public class DbNodeServiceImplTest extends BaseNodeServiceTest
private TransactionService txnService;
private NodeDaoService nodeDaoService;
private DictionaryService dictionaryService;
+ private NodeService mlAwareNodeService;
protected NodeService getNodeService()
{
@@ -64,6 +67,7 @@ public class DbNodeServiceImplTest extends BaseNodeServiceTest
txnService = (TransactionService) applicationContext.getBean("transactionComponent");
nodeDaoService = (NodeDaoService) applicationContext.getBean("nodeDaoService");
dictionaryService = (DictionaryService) applicationContext.getBean("dictionaryService");
+ mlAwareNodeService = (NodeService) applicationContext.getBean("mlAwareNodeService");
}
/**
@@ -293,4 +297,52 @@ public class DbNodeServiceImplTest extends BaseNodeServiceTest
assertTrue("Multi-valued buried content data not present in results",
allContentDatas.contains(contentDataMultiple));
}
+
+ public void testMLTextValues() throws Exception
+ {
+ // Set the server default locale
+ Locale.setDefault(Locale.ENGLISH);
+
+ MLText mlTextProperty = new MLText();
+ mlTextProperty.addValue(Locale.ENGLISH, "Very good!");
+ mlTextProperty.addValue(Locale.FRENCH, "Très bon!");
+ mlTextProperty.addValue(Locale.GERMAN, "Sehr gut!");
+
+ nodeService.setProperty(
+ rootNodeRef,
+ BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE,
+ mlTextProperty);
+
+ // Check filterered property retrieval
+ Serializable textValueFiltered = nodeService.getProperty(
+ rootNodeRef,
+ BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE);
+ assertEquals(
+ "Default locale value not taken for ML text",
+ mlTextProperty.getValue(Locale.ENGLISH),
+ textValueFiltered);
+
+ // Check filtered mass property retrieval
+ Map propertiesFiltered = nodeService.getProperties(rootNodeRef);
+ assertEquals(
+ "Default locale value not taken for ML text in Map",
+ mlTextProperty.getValue(Locale.ENGLISH),
+ propertiesFiltered.get(BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE));
+
+ // Check direct property retrieval
+ Serializable textValueDirect = mlAwareNodeService.getProperty(
+ rootNodeRef,
+ BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE);
+ assertEquals(
+ "MLText type not returned direct",
+ mlTextProperty,
+ textValueDirect);
+
+ // Check filtered mass property retrieval
+ Map propertiesDirect = mlAwareNodeService.getProperties(rootNodeRef);
+ assertEquals(
+ "MLText type not returned direct in Map",
+ mlTextProperty,
+ propertiesDirect.get(BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE));
+ }
}
diff --git a/source/java/org/alfresco/service/cmr/dictionary/DataTypeDefinition.java b/source/java/org/alfresco/service/cmr/dictionary/DataTypeDefinition.java
index 31cf6034e9..f8d04f96f7 100644
--- a/source/java/org/alfresco/service/cmr/dictionary/DataTypeDefinition.java
+++ b/source/java/org/alfresco/service/cmr/dictionary/DataTypeDefinition.java
@@ -34,6 +34,7 @@ public interface DataTypeDefinition
//
public QName ANY = QName.createQName(NamespaceService.DICTIONARY_MODEL_1_0_URI, "any");
public QName TEXT = QName.createQName(NamespaceService.DICTIONARY_MODEL_1_0_URI, "text");
+ public QName MLTEXT = QName.createQName(NamespaceService.DICTIONARY_MODEL_1_0_URI, "mltext");
public QName CONTENT = QName.createQName(NamespaceService.DICTIONARY_MODEL_1_0_URI, "content");
public QName INT = QName.createQName(NamespaceService.DICTIONARY_MODEL_1_0_URI, "int");
public QName LONG = QName.createQName(NamespaceService.DICTIONARY_MODEL_1_0_URI, "long");
diff --git a/source/java/org/alfresco/service/cmr/repository/MLText.java b/source/java/org/alfresco/service/cmr/repository/MLText.java
new file mode 100644
index 0000000000..4205761c48
--- /dev/null
+++ b/source/java/org/alfresco/service/cmr/repository/MLText.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2006 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.service.cmr.repository;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.alfresco.i18n.I18NUtil;
+
+/**
+ * Class to represent a multilingual (ML) text value.
+ *
+ * The language codes used should conform to the
+ * {@linkplain http://www.loc.gov/standards/iso639-2/php/English_list.php ISO639-2}
+ * language code standard, although there is no enforcement of the standard in this
+ * class.
+ *
+ * This is a simple extension of a HashMap
with a few convenience methods.
+ *
+ * @see ISO639-2
+ *
+ * @author Philippe Dubois
+ * @author Derek Hulley
+ */
+public class MLText extends HashMap
+{
+ private static final long serialVersionUID = -3696135175650511841L;
+
+ private Locale defaultLocale;
+
+ public MLText()
+ {
+ super(3, 0.75F);
+ }
+
+ /**
+ * Construct an instance with a value corresponding to the current context locale.
+ *
+ * @param value the value for the current default locale
+ *
+ * @see I18NUtil#getLocale()
+ * @see #MLText(Locale, String)
+ * @see #getDefaultValue()
+ */
+ public MLText(String value)
+ {
+ this(I18NUtil.getLocale(), value);
+ }
+
+ /**
+ * Construct an instance with a value for the given locale.
+ *
+ * @param locale the locale
+ * @param value the value
+ *
+ * @see #getDefaultValue()
+ */
+ public MLText(Locale locale, String value)
+ {
+ super(3, 0.75F);
+ defaultLocale = locale;
+ super.put(locale, value);
+ }
+
+ /**
+ * @return Returns all the language locales defined in the text
+ */
+ public Set getLocales()
+ {
+ return keySet();
+ }
+
+ /**
+ * @return Returns all the values stored
+ */
+ public Collection getValues()
+ {
+ return values();
+ }
+
+ /**
+ * Add a multilingual text value
+ *
+ * @param locale the language locale
+ * @param value the multilingual text
+ */
+ public void addValue(Locale locale, String value)
+ {
+ put(locale, value);
+ }
+
+ /**
+ * Retrieve a multilingual text value
+ *
+ * @param locale the language locale
+ */
+ public String getValue(Locale locale)
+ {
+ return get(locale);
+ }
+
+ /**
+ * Retrieves a default value from the set of available locales.
+ *
+ * @see I18NUtil#getLocale()
+ * @see #getClosestValue(Locale)
+ */
+ public String getDefaultValue()
+ {
+ Locale locale = I18NUtil.getLocale();
+ return getClosestValue(locale);
+ }
+
+ /**
+ * The given locale is used to search for a matching value according to:
+ *
+ * - An exact locale match
+ * - A match of locale ISO language codes
+ * - The value for the locale provided in the {@link MLText#MLText(Locale, String) constructor}
+ * - An arbitrary value
+ * - null
+ *
+ *
+ * @param locale the locale to use as the starting point of the value search
+ * @return Returns a default String value or null if one isn't available.
+ * null will only be returned if there are no values associated with
+ * this instance. With or without a match, the return value may be null.
+ */
+ public String getClosestValue(Locale locale)
+ {
+ if (this.size() == 0)
+ {
+ return null;
+ }
+ // Is there an exact match?
+ if (containsKey(locale))
+ {
+ return get(locale);
+ }
+ // Hunt for a similar language
+ Map.Entry lastEntry = null;
+ for (Map.Entry entry : this.entrySet())
+ {
+ lastEntry = entry; // Keep in case we need an arbitrary value later
+ Locale mapLocale = entry.getKey();
+ if (mapLocale == null)
+ {
+ continue;
+ }
+ if (mapLocale.getLanguage().equals(locale.getLanguage()))
+ {
+ // we found a language match
+ return entry.getValue();
+ }
+ }
+ // Nothing found. What about locale as per constructor?
+ if (containsKey(defaultLocale))
+ {
+ return get(defaultLocale);
+ }
+ // Still nothing. Just get a value.
+ return lastEntry.getValue();
+ }
+
+ /**
+ * Remove a multilingual text value
+ *
+ * @param locale the language locale
+ */
+ public void removeValue(Locale locale)
+ {
+ remove(locale);
+ }
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/service/cmr/repository/MLTextTest.java b/source/java/org/alfresco/service/cmr/repository/MLTextTest.java
new file mode 100644
index 0000000000..8248be4439
--- /dev/null
+++ b/source/java/org/alfresco/service/cmr/repository/MLTextTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2006 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.service.cmr.repository;
+
+import java.util.Locale;
+
+import junit.framework.TestCase;
+
+/**
+ * @see org.alfresco.service.cmr.repository.MLText
+ *
+ * @author Derek Hulley
+ */
+public class MLTextTest extends TestCase
+{
+ MLText mlText;
+
+ @Override
+ protected void setUp()
+ {
+ mlText = new MLText(Locale.CANADA_FRENCH, Locale.CANADA_FRENCH.toString());
+ mlText.addValue(Locale.US, Locale.US.toString());
+ mlText.addValue(Locale.UK, Locale.UK.toString());
+ mlText.addValue(Locale.FRENCH, Locale.FRENCH.toString());
+ mlText.addValue(Locale.CHINESE, Locale.CHINESE.toString());
+ }
+
+ public void testGetByLocale()
+ {
+ // check each value
+ assertNull("Expected nothing for German", mlText.getValue(Locale.GERMAN));
+ assertEquals(Locale.US.toString(), mlText.get(Locale.US));
+ assertEquals(Locale.UK.toString(), mlText.get(Locale.UK));
+ assertNull("Expected nothing for French general", mlText.getValue(Locale.FRENCH));
+// assertEquals("Expected Canada French to be found", Locale.CANADA_FRENCH.toString(),
+ }
+}
diff --git a/source/java/org/alfresco/service/cmr/repository/datatype/DefaultTypeConverter.java b/source/java/org/alfresco/service/cmr/repository/datatype/DefaultTypeConverter.java
index ff00b2dc7a..b1421ef95b 100644
--- a/source/java/org/alfresco/service/cmr/repository/datatype/DefaultTypeConverter.java
+++ b/source/java/org/alfresco/service/cmr/repository/datatype/DefaultTypeConverter.java
@@ -35,6 +35,7 @@ import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.EntityRef;
+import org.alfresco.service.cmr.repository.MLText;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.namespace.QName;
@@ -218,31 +219,31 @@ public class DefaultTypeConverter
});
INSTANCE.addConverter(String.class, NodeRef.class, new TypeConverter.Converter()
- {
- public NodeRef convert(String source)
- {
- return new NodeRef(source);
- }
-
- });
+ {
+ public NodeRef convert(String source)
+ {
+ return new NodeRef(source);
+ }
+
+ });
INSTANCE.addConverter(String.class, ChildAssociationRef.class, new TypeConverter.Converter()
- {
- public ChildAssociationRef convert(String source)
- {
- return new ChildAssociationRef(source);
- }
-
- });
+ {
+ public ChildAssociationRef convert(String source)
+ {
+ return new ChildAssociationRef(source);
+ }
+
+ });
INSTANCE.addConverter(String.class, AssociationRef.class, new TypeConverter.Converter()
- {
- public AssociationRef convert(String source)
- {
- return new AssociationRef(source);
- }
-
- });
+ {
+ public AssociationRef convert(String source)
+ {
+ return new AssociationRef(source);
+ }
+
+ });
INSTANCE.addConverter(String.class, InputStream.class, new TypeConverter.Converter()
{
@@ -259,6 +260,27 @@ public class DefaultTypeConverter
}
});
+ INSTANCE.addConverter(String.class, MLText.class, new TypeConverter.Converter()
+ {
+ public MLText convert(String source)
+ {
+ return new MLText(source);
+ }
+ });
+
+
+ //
+ // From MLText
+ //
+
+ INSTANCE.addConverter(MLText.class, String.class, new TypeConverter.Converter()
+ {
+ public String convert(MLText source)
+ {
+ return source.getDefaultValue();
+ }
+ });
+
//
// From enum
diff --git a/source/java/org/alfresco/service/cmr/repository/datatype/DefaultTypeConverterTest.java b/source/java/org/alfresco/service/cmr/repository/datatype/DefaultTypeConverterTest.java
index e01295c735..3314d1dca2 100644
--- a/source/java/org/alfresco/service/cmr/repository/datatype/DefaultTypeConverterTest.java
+++ b/source/java/org/alfresco/service/cmr/repository/datatype/DefaultTypeConverterTest.java
@@ -20,9 +20,11 @@ import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
+import java.util.Locale;
import junit.framework.TestCase;
+import org.alfresco.service.cmr.repository.MLText;
import org.alfresco.util.ISO8601DateFormat;
public class DefaultTypeConverterTest extends TestCase
@@ -88,6 +90,9 @@ public class DefaultTypeConverterTest extends TestCase
assertEquals(ISO8601DateFormat.format(date), DefaultTypeConverter.INSTANCE.convert(String.class, date));
assertEquals("P0Y25D", DefaultTypeConverter.INSTANCE.convert(String.class, new Duration("P0Y25D")));
assertEquals("woof", DefaultTypeConverter.INSTANCE.convert(String.class, "woof"));
+ MLText mlText = new MLText("woof");
+ mlText.addValue(Locale.SIMPLIFIED_CHINESE, "缂");
+ assertEquals("woof", DefaultTypeConverter.INSTANCE.convert(String.class, mlText));
}
public void testFromString()
@@ -106,6 +111,9 @@ public class DefaultTypeConverterTest extends TestCase
assertEquals("2004-03-12T00:00:00.000Z", ISO8601DateFormat.format(DefaultTypeConverter.INSTANCE.convert(Date.class, "2004-03-12T00:00:00.000Z")));
assertEquals(new Duration("P25D"), DefaultTypeConverter.INSTANCE.convert(Duration.class, "P25D"));
assertEquals("woof", DefaultTypeConverter.INSTANCE.convert(String.class, "woof"));
+
+ MLText converted = DefaultTypeConverter.INSTANCE.convert(MLText.class, "woof");
+ assertEquals("woof", converted.getValue(Locale.getDefault()));
}
public void testPrimativeAccessors()