First cut of multilingual support at the NodeService level

- MLText type is persisted as a Serializable (TODO: investigate alternative storage)
 - 'nodeService' bean is filtered according to the default locale to provide only String return properties
 - Go direct to the 'mlAwareNodeService' bean to get access to unfiltered MLText properties
 - TODO: Proper value searches respecting Locale hierarchy


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4526 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2006-12-06 15:30:30 +00:00
parent 8b65510d6f
commit 5a871bcdd4
13 changed files with 520 additions and 36 deletions

View File

@@ -366,6 +366,8 @@ public class PropertyValue implements Cloneable, Serializable
valueTypesByPropertyType.put(DataTypeDefinition.CATEGORY, ValueType.NODEREF);
valueTypesByPropertyType.put(DataTypeDefinition.CONTENT, ValueType.CONTENT);
valueTypesByPropertyType.put(DataTypeDefinition.TEXT, ValueType.STRING);
// TODO: Re-examine storage of MLTEXT data type
valueTypesByPropertyType.put(DataTypeDefinition.MLTEXT, ValueType.SERIALIZABLE);
valueTypesByPropertyType.put(DataTypeDefinition.NODE_REF, ValueType.NODEREF);
valueTypesByPropertyType.put(DataTypeDefinition.CHILD_ASSOC_REF, ValueType.CHILD_ASSOC_REF);
valueTypesByPropertyType.put(DataTypeDefinition.ASSOC_REF, ValueType.ASSOC_REF);

View File

@@ -301,7 +301,7 @@ public class FileFolderPerformanceTester extends TestCase
}
}
}
public void test_1_ordered_1_10() throws Exception
{
buildStructure(rootFolderRef, 1, false, 1, 10, null);
@@ -321,16 +321,16 @@ public class FileFolderPerformanceTester extends TestCase
// {
// buildStructure(rootFolderRef, 4, true, 10, 100, new double[] {0.25, 0.50, 0.75});
// }
// public void test_1_ordered_100_100() throws Exception
// {
// buildStructure(
// rootFolderRef,
// 1,
// false,
// 100,
// 100,
// new double[] {0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90});
// }
public void test_1_ordered_100_100() throws Exception
{
buildStructure(
rootFolderRef,
1,
false,
100,
100,
new double[] {0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90});
}
// public void test_1_shuffled_10_400() throws Exception
// {
// buildStructure(

View File

@@ -51,6 +51,7 @@ import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.CyclicChildRelationshipException;
import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
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.cmr.repository.Path;
@@ -98,6 +99,7 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
public static final QName PROP_QNAME_FLOAT_VALUE = QName.createQName(NAMESPACE, "floatValue");
public static final QName PROP_QNAME_DOUBLE_VALUE = QName.createQName(NAMESPACE, "doubleValue");
public static final QName PROP_QNAME_STRING_VALUE = QName.createQName(NAMESPACE, "stringValue");
public static final QName PROP_QNAME_ML_TEXT_VALUE = QName.createQName(NAMESPACE, "mlTextValue");
public static final QName PROP_QNAME_DATE_VALUE = QName.createQName(NAMESPACE, "dateValue");
public static final QName PROP_QNAME_SERIALIZABLE_VALUE = QName.createQName(NAMESPACE, "serializableValue");
public static final QName PROP_QNAME_NODEREF_VALUE = QName.createQName(NAMESPACE, "nodeRefValue");
@@ -1005,6 +1007,7 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
properties.put(PROP_QNAME_FLOAT_VALUE, 123.0F);
properties.put(PROP_QNAME_DOUBLE_VALUE, 123.0);
properties.put(PROP_QNAME_STRING_VALUE, "123.0");
properties.put(PROP_QNAME_ML_TEXT_VALUE, "This is ML text in the default language");
properties.put(PROP_QNAME_DATE_VALUE, new Date());
properties.put(PROP_QNAME_SERIALIZABLE_VALUE, "456");
properties.put(PROP_QNAME_NODEREF_VALUE, rootNodeRef);

View File

@@ -227,6 +227,10 @@
<type>d:text</type>
<mandatory>true</mandatory>
</property>
<property name="test:mlTextValue">
<type>d:mltext</type>
<mandatory>true</mandatory>
</property>
<property name="test:dateValue">
<type>d:date</type>
<mandatory>true</mandatory>

View File

@@ -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.
* <p>
* 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.
* <p>
* 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<QName, Serializable> properties = (Map<QName, Serializable>) invocation.proceed();
Map<QName, Serializable> convertedProperties = new HashMap<QName, Serializable>(properties.size() * 2);
// Check each return value type
for (Map.Entry<QName, Serializable> 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;
}
}

View File

@@ -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<QName, Serializable> 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<QName, Serializable> propertiesDirect = mlAwareNodeService.getProperties(rootNodeRef);
assertEquals(
"MLText type not returned direct in Map",
mlTextProperty,
propertiesDirect.get(BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE));
}
}