Merging from EC-MC: Checkpoint before refactor

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5744 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2007-05-22 05:03:16 +00:00
parent 2d461f5dd9
commit d818c54e99
22 changed files with 644 additions and 416 deletions

View File

@@ -30,6 +30,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.alfresco.error.AlfrescoRuntimeException;
@@ -37,6 +38,7 @@ import org.alfresco.model.ContentModel;
import org.alfresco.repo.search.QueryParameterDefImpl;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.ml.MultilingualContentService;
import org.alfresco.service.cmr.model.FileExistsException;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
@@ -121,6 +123,7 @@ public class FileFolderServiceImpl implements FileFolderService
private CopyService copyService;
private SearchService searchService;
private ContentService contentService;
private MultilingualContentService multilingualContentService;
private MimetypeService mimetypeService;
// TODO: Replace this with a more formal means of identifying "system" folders (i.e. aspect or UUID)
@@ -164,6 +167,11 @@ public class FileFolderServiceImpl implements FileFolderService
this.contentService = contentService;
}
public void setMultilingualContentService(MultilingualContentService multilingualContentService)
{
this.multilingualContentService = multilingualContentService;
}
public void setMimetypeService(MimetypeService mimetypeService)
{
this.mimetypeService = mimetypeService;
@@ -198,7 +206,7 @@ public class FileFolderServiceImpl implements FileFolderService
List<FileInfo> results = new ArrayList<FileInfo>(nodeRefs.size());
for (NodeRef nodeRef : nodeRefs)
{
FileInfo fileInfo = toFileInfo(nodeRef);
FileInfo fileInfo = toFileInfo(nodeRef, true);
results.add(fileInfo);
}
return results;
@@ -207,7 +215,7 @@ public class FileFolderServiceImpl implements FileFolderService
/**
* Helper method to convert a node reference instance to a file info
*/
private FileInfo toFileInfo(NodeRef nodeRef) throws InvalidTypeException
private FileInfo toFileInfo(NodeRef nodeRef, boolean addTranslations) throws InvalidTypeException
{
// get the file attributes
Map<QName, Serializable> properties = nodeService.getProperties(nodeRef);
@@ -215,8 +223,33 @@ public class FileFolderServiceImpl implements FileFolderService
QName typeQName = nodeService.getType(nodeRef);
boolean isFolder = isFolder(typeQName);
Map<Locale, FileInfo> translations = null;
if (!isFolder && addTranslations)
{
// Get any translations
translations = new HashMap<Locale, FileInfo>(13);
// Check for the ML aspect
if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT))
{
// Get all the translations
Map<Locale, NodeRef> translationsToConvert = multilingualContentService.getTranslations(nodeRef);
for (Map.Entry<Locale, NodeRef> entry : translationsToConvert.entrySet())
{
Locale locale = entry.getKey();
NodeRef nodeRefToConvert = entry.getValue();
FileInfo convertedFileInfo = toFileInfo(nodeRefToConvert, false);
// Add to map
translations.put(locale, convertedFileInfo);
}
}
}
else
{
translations = Collections.<Locale, FileInfo>emptyMap();
}
// construct the file info and add to the results
FileInfo fileInfo = new FileInfoImpl(nodeRef, isFolder, properties);
FileInfo fileInfo = new FileInfoImpl(nodeRef, isFolder, properties, translations);
// done
return fileInfo;
}
@@ -542,7 +575,7 @@ public class FileFolderServiceImpl implements FileFolderService
private FileInfo moveOrCopy(NodeRef sourceNodeRef, NodeRef targetParentRef, String newName, boolean move) throws FileExistsException, FileNotFoundException
{
// get file/folder in its current state
FileInfo beforeFileInfo = toFileInfo(sourceNodeRef);
FileInfo beforeFileInfo = toFileInfo(sourceNodeRef, true);
// check the name - null means keep the existing name
if (newName == null)
{
@@ -634,7 +667,7 @@ public class FileFolderServiceImpl implements FileFolderService
}
// get the details after the operation
FileInfo afterFileInfo = toFileInfo(targetNodeRef);
FileInfo afterFileInfo = toFileInfo(targetNodeRef, true);
// done
if (logger.isDebugEnabled())
{
@@ -705,11 +738,11 @@ public class FileFolderServiceImpl implements FileFolderService
}
NodeRef nodeRef = assocRef.getChildRef();
FileInfo fileInfo = toFileInfo(nodeRef);
FileInfo fileInfo = toFileInfo(nodeRef, true);
// done
if (logger.isDebugEnabled())
{
FileInfo parentFileInfo = toFileInfo(parentNodeRef);
FileInfo parentFileInfo = toFileInfo(parentNodeRef, false);
logger.debug("Created: \n" +
" parent: " + parentFileInfo + "\n" +
" created: " + fileInfo);
@@ -755,7 +788,7 @@ public class FileFolderServiceImpl implements FileFolderService
}
}
// done
FileInfo fileInfo = toFileInfo(currentParentRef);
FileInfo fileInfo = toFileInfo(currentParentRef, true);
return fileInfo;
}
@@ -790,7 +823,7 @@ public class FileFolderServiceImpl implements FileFolderService
continue;
}
// we found the root and expect to be building the path up
FileInfo pathInfo = toFileInfo(childNodeRef);
FileInfo pathInfo = toFileInfo(childNodeRef, true);
results.add(pathInfo);
}
// check that we found the root
@@ -861,7 +894,7 @@ public class FileFolderServiceImpl implements FileFolderService
{
try
{
return toFileInfo(nodeRef);
return toFileInfo(nodeRef, true);
}
catch (InvalidTypeException e)
{
@@ -871,7 +904,7 @@ public class FileFolderServiceImpl implements FileFolderService
public ContentReader getReader(NodeRef nodeRef)
{
FileInfo fileInfo = toFileInfo(nodeRef);
FileInfo fileInfo = toFileInfo(nodeRef, false);
if (fileInfo.isFolder())
{
throw new InvalidTypeException("Unable to get a content reader for a folder: " + fileInfo);
@@ -881,7 +914,7 @@ public class FileFolderServiceImpl implements FileFolderService
public ContentWriter getWriter(NodeRef nodeRef)
{
FileInfo fileInfo = toFileInfo(nodeRef);
FileInfo fileInfo = toFileInfo(nodeRef, false);
if (fileInfo.isFolder())
{
throw new InvalidTypeException("Unable to get a content writer for a folder: " + fileInfo);

View File

@@ -25,7 +25,9 @@
package org.alfresco.repo.model.filefolder;
import java.io.Serializable;
import java.util.Collections;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import org.alfresco.model.ContentModel;
@@ -44,6 +46,7 @@ public class FileInfoImpl implements FileInfo
{
private NodeRef nodeRef;
private NodeRef linkNodeRef;
private Map<Locale, FileInfo> translations;
private boolean isFolder;
private boolean isLink;
private Map<QName, Serializable> properties;
@@ -52,13 +55,34 @@ public class FileInfoImpl implements FileInfo
* Package-level constructor
*/
/* package */ FileInfoImpl(NodeRef nodeRef, boolean isFolder, Map<QName, Serializable> properties)
{
this(nodeRef, isFolder, properties, Collections.<Locale, FileInfo>emptyMap());
}
/**
* Package-level constructor
*
* @param translations a map of translations including this instance. It may be null.
*/
/* package */ FileInfoImpl(
NodeRef nodeRef,
boolean isFolder,
Map<QName, Serializable> properties,
Map<Locale, FileInfo> translations)
{
this.nodeRef = nodeRef;
this.isFolder = isFolder;
this.properties = properties;
if (translations == null || isFolder)
{
this.translations = Collections.<Locale, FileInfo>emptyMap();
}
else
{
this.translations = translations;
}
// Check if this is a link node
if ( properties.containsKey( ContentModel.PROP_LINK_DESTINATION))
{
isLink = true;
@@ -66,6 +90,31 @@ public class FileInfoImpl implements FileInfo
}
}
/**
* @see #getNodeRef()
* @see NodeRef#equals(Object)
*/
@Override
public boolean equals(Object obj)
{
if (obj == null || this.getClass().isInstance(obj))
{
return false;
}
FileInfoImpl that = (FileInfoImpl) obj;
return (this.getNodeRef().equals(that.getNodeRef()));
}
/**
* @see #getNodeRef()
* @see NodeRef#hashCode()
*/
@Override
public int hashCode()
{
return getNodeRef().hashCode();
}
@Override
public String toString()
{
@@ -81,6 +130,8 @@ public class FileInfoImpl implements FileInfo
sb.append(linkNodeRef);
}
sb.append(", translations=").append(translations.size());
sb.append("]");
return sb.toString();
}
@@ -105,6 +156,11 @@ public class FileInfoImpl implements FileInfo
return linkNodeRef;
}
public Map<Locale, FileInfo> getTranslations()
{
return translations;
}
public String getName()
{
return (String) properties.get(ContentModel.PROP_NAME);

View File

@@ -0,0 +1,256 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU 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 General Public License for more details.
* You should have received a copy of the GNU 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.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.model.filefolder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.ml.MultilingualContentService;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* An interceptor that replaces files nodes with their equivalent
* translations according to the locale. It is to be used with the
* {@link FileFolderService}.
*
* @since 2.1
* @author Derek Hulley
*/
public class MLTranslationInterceptor implements MethodInterceptor
{
/**
* Names of methods that return a <code>List</code> or <code>FileInfo</code> instances.
*/
private static final Set<String> METHOD_NAMES_LIST;
/**
* Names of methods that return a <code>FileInfo</code>.
*/
private static final Set<String> METHOD_NAMES_SINGLE;
/**
* Names of methods that don't need interception. This is used to catch any new methods
* added to the interface.
*/
private static final Set<String> METHOD_NAMES_OTHER;
static
{
METHOD_NAMES_LIST = new HashSet<String>(13);
METHOD_NAMES_LIST.add("list");
METHOD_NAMES_LIST.add("listFiles");
METHOD_NAMES_LIST.add("listFolders");
METHOD_NAMES_LIST.add("search");
METHOD_NAMES_LIST.add("getNamePath");
METHOD_NAMES_SINGLE = new HashSet<String>(13);
METHOD_NAMES_SINGLE.add("searchSimple");
METHOD_NAMES_SINGLE.add("rename");
METHOD_NAMES_SINGLE.add("move");
METHOD_NAMES_SINGLE.add("copy");
METHOD_NAMES_SINGLE.add("create");
METHOD_NAMES_SINGLE.add("makeFolders");
METHOD_NAMES_SINGLE.add("getNamePath");
METHOD_NAMES_SINGLE.add("resolveNamePath");
METHOD_NAMES_SINGLE.add("getFileInfo");
METHOD_NAMES_OTHER = new HashSet<String>(13);
METHOD_NAMES_OTHER.add("delete");
METHOD_NAMES_OTHER.add("getReader");
METHOD_NAMES_OTHER.add("getWriter");
}
private static Log logger = LogFactory.getLog(MLTranslationInterceptor.class);
private NodeService nodeService;
private MultilingualContentService multilingualContentService;
/**
* Constructor.
*/
public MLTranslationInterceptor()
{
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setMultilingualContentService(MultilingualContentService multilingualContentService)
{
this.multilingualContentService = multilingualContentService;
}
/**
* Converts the node referenice where an alternative translation should be used.
*
* @param nodeRef the basic nodeRef
* @return Returns the replacement if required
*/
private NodeRef getTranslatedNodeRef(NodeRef nodeRef)
{
// Ignore null
if (nodeRef == null)
{
return nodeRef;
}
// Ignore everything without the correct aspect
if (!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_DOCUMENT))
{
return nodeRef;
}
// Find the translation
Map<Locale, NodeRef> translations = multilingualContentService.getTranslations(nodeRef);
Locale filterLocale = I18NUtil.getContentLocaleOrNull();
Set<Locale> possibleLocales = translations.keySet();
Locale localeToUse = I18NUtil.getNearestLocale(filterLocale, possibleLocales);
// Select the node
NodeRef translatedNodeRef = translations.get(localeToUse);
// Done
if (logger.isDebugEnabled())
{
if (nodeRef.equals(translatedNodeRef))
{
logger.debug("NodeRef substitution: " + nodeRef + " --> " + translatedNodeRef);
}
else
{
logger.debug("NodeRef substitution: " + nodeRef + " (no change)");
}
}
return nodeRef;
}
/**
* Converts the file info where an alternative translation should be used.
*
* @param fileInfo the basic file or folder info
* @return Returns a replacement if required
*
* @see FileInfo#getTranslations()
*/
private FileInfo getTranslatedFileInfo(FileInfo fileInfo)
{
// Ignore null
if (fileInfo == null)
{
return null;
}
// Ignore folders
if (fileInfo.isFolder())
{
return fileInfo;
}
// Ignore files without translations
Map<Locale, FileInfo> translations = fileInfo.getTranslations();
if (translations.size() == 0)
{
return fileInfo;
}
// Get the locale to use
Set<Locale> possibleLocales = translations.keySet();
Locale filterLocale = I18NUtil.getContentLocaleOrNull();
Locale localeToUse = I18NUtil.getNearestLocale(filterLocale, possibleLocales);
FileInfo translatedFileInfo = translations.get(localeToUse);
// Done
if (logger.isDebugEnabled())
{
if (fileInfo.equals(translatedFileInfo))
{
logger.debug("FileInfo substitution: " + fileInfo + " --> " + translatedFileInfo);
}
else
{
logger.debug("FileInfo substitution: " + fileInfo + " (no change)");
}
}
return translatedFileInfo;
}
@SuppressWarnings("unchecked")
public Object invoke(MethodInvocation invocation) throws Throwable
{
Object ret = null;
String methodName = invocation.getMethod().getName();
if (METHOD_NAMES_LIST.contains(methodName))
{
List<FileInfo> fileInfos = (List<FileInfo>) invocation.proceed();
// Compile a set to ensure we don't get duplicates
Map<FileInfo, FileInfo> translatedFileInfos = new HashMap<FileInfo, FileInfo>(17);
for (FileInfo fileInfo : fileInfos)
{
FileInfo translatedFileInfo = getTranslatedFileInfo(fileInfo);
// Add this to the set
translatedFileInfos.put(fileInfo, translatedFileInfo);
}
// Convert the set back to a list
List<FileInfo> orderedResults = new ArrayList<FileInfo>(fileInfos.size());
for (FileInfo info : fileInfos)
{
orderedResults.add(translatedFileInfos.get(info));
}
ret = orderedResults;
}
else if (METHOD_NAMES_SINGLE.contains(methodName))
{
Object obj = invocation.proceed();
if (obj instanceof FileInfo)
{
FileInfo fileInfo = (FileInfo) obj;
ret = getTranslatedFileInfo(fileInfo);
}
else if (obj instanceof NodeRef)
{
NodeRef nodeRef = (NodeRef) obj;
ret = getTranslatedNodeRef(nodeRef);
}
}
else if (METHOD_NAMES_OTHER.contains(methodName))
{
// There is nothing to do
ret = invocation.proceed();
}
else
{
throw new RuntimeException("Method not handled by interceptor: " + methodName);
}
// Done
return ret;
}
}