From 1c928e4c9f1ee6a950b077aeeeda3773e6dbac47 Mon Sep 17 00:00:00 2001 From: Ray Gauss Date: Wed, 30 Jan 2013 20:32:55 +0000 Subject: [PATCH] Merged BRANCHES/DEV/RGAUSS/HEAD-SOURCE-TARGET-TRANS-OPTIONS to HEAD: 45449: ALF-13254: TransformationOptions Should Have Separate Source and Target Options - Added SerializedTransformationOptionsAccessor interface which defines the methods used in the protected AbstractRenderingEngine.RenderContext class in a public manner - Changed AbstractRenderingEngine.RenderContext to implement SerializedTransformationOptionsAccessor - Added TransformationSourceOptions interface which also contains TransformationSourceOptionsSerializer interface which uses SerializedTransformationOptionsAccessor for deserialization - Added base AbstractTransformationSourceOptions class - Added PagedSourceOptions class which extends TransformationSourceOptions for start and end page options - Added TemporalSourceOptions class which extends TransformationSourceOptions for time-based offset and duration options - Changed TransformationOptions to contain TransformationSourceOptions held as a map with class as key - Changed ImageTransformationOptions to extend copyFrom - Changed ImageMagickContentTransformerWorker.getSourcePageRange to check for paged source options in the TransformationOptions passed in - Added ImageMagickContentTransformerTest.testPageSourceOptions to test null, default, page 2, and invalid options - Changed ThumbnailRenditionConvertor to iterate the transformationOptions.sourceOptionsList and use each serializer to add to the parameters - Changed AbstractTransformationRenderingEngine to iterate a list of TransformationSourceOptionsSerializers and use each to deserialize the RenderContext parameters and construct a TransformationSourceOptions object - Changed rendition-services-context.xml to set imageRenderingEngine's list of known sourceOptionsSerializers - Changed ThumbnailServiceImplParameterTest to test paged and temporal options - Added ThumbanailServiceImplTest.testCreateRenditionThumbnailFromPdfPage2 which tests grabbing the second page of a PDF git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@46062 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../alfresco/rendition-services-context.xml | 6 + .../ImageMagickContentTransformerTest.java | 98 +++++++++ .../ImageMagickContentTransformerWorker.java | 43 +++- .../magick/ImageTransformationOptions.java | 18 +- .../executer/AbstractRenderingEngine.java | 25 +-- ...AbstractTransformationRenderingEngine.java | 26 +++ .../ThumbnailRenditionConvertor.java | 9 + .../ThumbnailServiceImplParameterTest.java | 16 ++ .../thumbnail/ThumbnailServiceImplTest.java | 34 +++ .../AbstractTransformationSourceOptions.java | 117 ++++++++++ .../cmr/repository/PagedSourceOptions.java | 203 ++++++++++++++++++ ...rializedTransformationOptionsAccessor.java | 66 ++++++ .../cmr/repository/TemporalSourceOptions.java | 173 +++++++++++++++ .../cmr/repository/TransformationOptions.java | 93 +++++++- .../TransformationSourceOptions.java | 105 +++++++++ 15 files changed, 1005 insertions(+), 27 deletions(-) create mode 100644 source/java/org/alfresco/service/cmr/repository/AbstractTransformationSourceOptions.java create mode 100644 source/java/org/alfresco/service/cmr/repository/PagedSourceOptions.java create mode 100644 source/java/org/alfresco/service/cmr/repository/SerializedTransformationOptionsAccessor.java create mode 100644 source/java/org/alfresco/service/cmr/repository/TemporalSourceOptions.java create mode 100644 source/java/org/alfresco/service/cmr/repository/TransformationSourceOptions.java diff --git a/config/alfresco/rendition-services-context.xml b/config/alfresco/rendition-services-context.xml index 79d9b70c13..a56d83da6e 100644 --- a/config/alfresco/rendition-services-context.xml +++ b/config/alfresco/rendition-services-context.xml @@ -138,6 +138,12 @@ + + + + + + 0); + } + } + + public void testPageSourceOptions() throws Exception + { + // Test empty source options + ImageTransformationOptions options = new ImageTransformationOptions(); + this.transform(MimetypeMap.MIMETYPE_PDF, MimetypeMap.MIMETYPE_IMAGE_PNG, options); + + // Test first page + options = new ImageTransformationOptions(); + List sourceOptionsList = new ArrayList(); + sourceOptionsList.add(PagedSourceOptions.getPage1Instance()); + options.setSourceOptionsList(sourceOptionsList); + this.transform(MimetypeMap.MIMETYPE_PDF, MimetypeMap.MIMETYPE_IMAGE_PNG, options); + + // Test second page + options = new ImageTransformationOptions(); + sourceOptionsList = new ArrayList(); + PagedSourceOptions sourceOptions = new PagedSourceOptions(); + sourceOptions.setStartPageNumber(2); + sourceOptions.setEndPageNumber(2); + sourceOptionsList.add(sourceOptions); + options.setSourceOptionsList(sourceOptionsList); + this.transform(MimetypeMap.MIMETYPE_PDF, MimetypeMap.MIMETYPE_IMAGE_PNG, options); + + // Test page range invalid for target type + options = new ImageTransformationOptions(); + sourceOptionsList = new ArrayList(); + sourceOptions = new PagedSourceOptions(); + sourceOptions.setStartPageNumber(1); + sourceOptions.setEndPageNumber(2); + sourceOptionsList.add(sourceOptions); + options.setSourceOptionsList(sourceOptionsList); + try { + this.transform(MimetypeMap.MIMETYPE_PDF, MimetypeMap.MIMETYPE_IMAGE_PNG, options); + fail("An exception regarding an invalid page range should have been thrown"); + } + catch (Exception e) + { + // failure expected + } + + // Test page out of range + options = new ImageTransformationOptions(); + sourceOptionsList = new ArrayList(); + sourceOptions = new PagedSourceOptions(); + sourceOptions.setStartPageNumber(3); + sourceOptions.setEndPageNumber(3); + sourceOptionsList.add(sourceOptions); + options.setSourceOptionsList(sourceOptionsList); + try { + this.transform(MimetypeMap.MIMETYPE_PDF, MimetypeMap.MIMETYPE_IMAGE_PNG, options); + fail("An exception regarding an invalid page range should have been thrown"); + } + catch (Exception e) + { + // failure expected + } + } + /** * Mock mimetype service which returns a limited set of mimetypes * as {@link AbstractContentTransformerTest#testAllConversions()} will diff --git a/source/java/org/alfresco/repo/content/transform/magick/ImageMagickContentTransformerWorker.java b/source/java/org/alfresco/repo/content/transform/magick/ImageMagickContentTransformerWorker.java index 787d3cc806..31ff8256ef 100644 --- a/source/java/org/alfresco/repo/content/transform/magick/ImageMagickContentTransformerWorker.java +++ b/source/java/org/alfresco/repo/content/transform/magick/ImageMagickContentTransformerWorker.java @@ -25,6 +25,7 @@ import java.util.Map; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.service.cmr.repository.ContentIOException; +import org.alfresco.service.cmr.repository.PagedSourceOptions; import org.alfresco.service.cmr.repository.TransformationOptions; import org.alfresco.util.exec.RuntimeExec; import org.alfresco.util.exec.RuntimeExec.ExecutionResult; @@ -295,13 +296,13 @@ public class ImageMagickContentTransformerWorker extends AbstractImageMagickCont } /** - * Determines whether or not page range is required for the given source and target mimetypes. + * Determines whether or not a single page range is required for the given source and target mimetypes. * * @param sourceMimetype * @param targetMimetype * @return whether or not a page range must be specified for the transformer to read the target files */ - private boolean isSourcePageRangeRequired(String sourceMimetype, String targetMimetype) + private boolean isSingleSourcePageRangeRequired(String sourceMimetype, String targetMimetype) { // Need a page source if we're transforming from PDF or TIFF to an image other than TIFF return ((sourceMimetype.equals(MimetypeMap.MIMETYPE_PDF) || @@ -322,7 +323,43 @@ public class ImageMagickContentTransformerWorker extends AbstractImageMagickCont */ private String getSourcePageRange(TransformationOptions options, String sourceMimetype, String targetMimetype) { - if (options.getPageLimit() == 1 || isSourcePageRangeRequired(sourceMimetype, targetMimetype)) + // Check for PagedContentSourceOptions in the options + if (options instanceof ImageTransformationOptions) + { + ImageTransformationOptions imageOptions = (ImageTransformationOptions) options; + PagedSourceOptions pagedSourceOptions = imageOptions.getSourceOptions(PagedSourceOptions.class); + if (pagedSourceOptions != null) + { + if (pagedSourceOptions.getStartPageNumber() != null && + pagedSourceOptions.getEndPageNumber() != null) + { + if (pagedSourceOptions.getStartPageNumber().equals(pagedSourceOptions.getEndPageNumber())) + { + return "[" + (pagedSourceOptions.getStartPageNumber() - 1) + "]"; + } + else + { + if (isSingleSourcePageRangeRequired(sourceMimetype, targetMimetype)) + { + throw new AlfrescoRuntimeException( + "A single page is required for targets of type " + targetMimetype); + } + return "[" + (pagedSourceOptions.getStartPageNumber() - 1) + + "-" + (pagedSourceOptions.getEndPageNumber() - 1) + "]"; + } + } + else + { + // TODO specified start to end of doc and start of doc to specified end not yet supported + // Just grab a single page specified by either start or end + if (pagedSourceOptions.getStartPageNumber() != null) + return "[" + (pagedSourceOptions.getStartPageNumber() - 1) + "]"; + if (pagedSourceOptions.getEndPageNumber() != null) + return "[" + (pagedSourceOptions.getEndPageNumber() - 1) + "]"; + } + } + } + if (options.getPageLimit() == 1 || isSingleSourcePageRangeRequired(sourceMimetype, targetMimetype)) { return "[0]"; } diff --git a/source/java/org/alfresco/repo/content/transform/magick/ImageTransformationOptions.java b/source/java/org/alfresco/repo/content/transform/magick/ImageTransformationOptions.java index f05044ada4..f9ebb42d57 100644 --- a/source/java/org/alfresco/repo/content/transform/magick/ImageTransformationOptions.java +++ b/source/java/org/alfresco/repo/content/transform/magick/ImageTransformationOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -143,4 +143,20 @@ public class ImageTransformationOptions extends TransformationOptions { this.autoOrient = autoOrient; } + + @Override + public void copyFrom(TransformationOptions origOptions) { + super.copyFrom(origOptions); + if (origOptions != null) + { + if (origOptions instanceof ImageTransformationOptions) + { + // Clone ImageTransformationOptions + this.setCommandOptions(((ImageTransformationOptions) origOptions).getCommandOptions()); + this.setResizeOptions(((ImageTransformationOptions) origOptions).getResizeOptions()); + this.setCropOptions(((ImageTransformationOptions) origOptions).getCropOptions()); + this.setAutoOrient(((ImageTransformationOptions) origOptions).isAutoOrient()); + } + } + } } diff --git a/source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngine.java b/source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngine.java index 5bfb0b91f4..db0e56e133 100644 --- a/source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngine.java +++ b/source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngine.java @@ -59,6 +59,7 @@ import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.SerializedTransformationOptionsAccessor; import org.alfresco.service.namespace.NamespaceException; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; @@ -732,7 +733,7 @@ public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase return result; } - protected class RenderingContext + protected class RenderingContext implements SerializedTransformationOptionsAccessor { private final NodeRef sourceNode; private final RenditionDefinition definition; @@ -792,31 +793,11 @@ public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase return this.definition; } - /** - * Gets the value for the named parameter from the . Checks the type of - * the parameter is correct and throws and Exception if it isn't. - * Returns null if the parameter value is null - * - * @param paramName the name of the parameter being checked. - * @param clazz the expected {@link Class} of the parameter value. - * @return the parameter value or null. - */ public T getCheckedParam(String paramName, Class clazz) { return AbstractRenderingEngine.getCheckedParam(paramName, clazz, definition); } - - /** - * Gets the value for the named parameter. Checks the type of the - * parameter is the same as the type of defaultValue and - * throws a {@link RenditionServiceException} if it isn't. Returns - * defaultValue if the parameter value is null - * - * @param - * @param paramName - * @param defaultValue - * @return - */ + public T getParamWithDefault(String paramName, T defaultValue) { return AbstractRenderingEngine.getParamWithDefault(paramName, defaultValue, definition); diff --git a/source/java/org/alfresco/repo/rendition/executer/AbstractTransformationRenderingEngine.java b/source/java/org/alfresco/repo/rendition/executer/AbstractTransformationRenderingEngine.java index 10706ef56a..b721f964ec 100644 --- a/source/java/org/alfresco/repo/rendition/executer/AbstractTransformationRenderingEngine.java +++ b/source/java/org/alfresco/repo/rendition/executer/AbstractTransformationRenderingEngine.java @@ -32,6 +32,8 @@ import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NoTransformerException; import org.alfresco.service.cmr.repository.TransformationOptionLimits; import org.alfresco.service.cmr.repository.TransformationOptions; +import org.alfresco.service.cmr.repository.TransformationSourceOptions; +import org.alfresco.service.cmr.repository.TransformationSourceOptions.TransformationSourceOptionsSerializer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -82,6 +84,18 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend private static final String TRANSFORMER_NOT_EXISTS_MESSAGE_PATTERN = "Transformer for '%s' source mime type and '%s' target mime type was not found. Operation can't be performed"; private static final String NOT_TRANSFORMABLE_MESSAGE_PATTERN = "Content not transformable for '%s' source mime type and '%s' target mime type. Operation can't be performed"; private static final String TRANSFORMING_ERROR_MESSAGE = "Some error occurred during document transforming. Error message: "; + + private Collection sourceOptionsSerializers; + + public Collection getSourceOptionsSerializers() + { + return sourceOptionsSerializers; + } + + public void setSourceOptionsSerializers(Collection sourceOptionsSerializers) + { + this.sourceOptionsSerializers = sourceOptionsSerializers; + } /* * (non-Javadoc) @@ -184,6 +198,18 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend { options.setPageLimit(pageLimit); } + + if (getSourceOptionsSerializers() != null) + { + for (TransformationSourceOptionsSerializer sourceSerializer : getSourceOptionsSerializers()) + { + TransformationSourceOptions sourceOptions = sourceSerializer.deserialize(context); + if (sourceOptions != null) + { + options.addSourceOptions(sourceOptions); + } + } + } return options; } diff --git a/source/java/org/alfresco/repo/thumbnail/ThumbnailRenditionConvertor.java b/source/java/org/alfresco/repo/thumbnail/ThumbnailRenditionConvertor.java index cf25c4233d..6502fb6e0c 100644 --- a/source/java/org/alfresco/repo/thumbnail/ThumbnailRenditionConvertor.java +++ b/source/java/org/alfresco/repo/thumbnail/ThumbnailRenditionConvertor.java @@ -33,6 +33,7 @@ import org.alfresco.repo.rendition.executer.ReformatRenderingEngine; import org.alfresco.service.cmr.rendition.RenditionDefinition; import org.alfresco.service.cmr.rendition.RenditionService; import org.alfresco.service.cmr.repository.TransformationOptions; +import org.alfresco.service.cmr.repository.TransformationSourceOptions; import org.alfresco.service.cmr.thumbnail.ThumbnailParentAssociationDetails; import org.alfresco.service.cmr.thumbnail.ThumbnailService; import org.alfresco.service.namespace.NamespaceService; @@ -185,6 +186,14 @@ public class ThumbnailRenditionConvertor parameters.put(ImageRenderingEngine.PARAM_ALLOW_ENLARGEMENT, allowEnlargement); } } + if (transformationOptions.getSourceOptionsList() != null) + { + for (TransformationSourceOptions sourceOptions : transformationOptions.getSourceOptionsList()) + { + sourceOptions.getSerializer().serialize(sourceOptions, parameters); + } + } + // TODO Handle RuntimeExecutableTransformationOptions return parameters; } diff --git a/source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplParameterTest.java b/source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplParameterTest.java index 87320ba925..33caa8d482 100644 --- a/source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplParameterTest.java +++ b/source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplParameterTest.java @@ -45,6 +45,10 @@ import org.alfresco.service.cmr.rendition.RenditionService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.PagedSourceOptions; +import org.alfresco.service.cmr.repository.PagedSourceOptions.PagedSourceOptionsSerializer; +import org.alfresco.service.cmr.repository.TemporalSourceOptions; +import org.alfresco.service.cmr.repository.TemporalSourceOptions.TemporalSourceOptionsSerializer; import org.alfresco.service.cmr.thumbnail.ThumbnailParentAssociationDetails; import org.alfresco.service.cmr.thumbnail.ThumbnailService; import org.alfresco.service.namespace.NamespaceService; @@ -135,6 +139,9 @@ public class ThumbnailServiceImplParameterTest parametersUnderTest.put(ImageRenderingEngine.PARAM_ALLOW_ENLARGEMENT, Boolean.TRUE); parametersUnderTest.put(AbstractRenderingEngine.PARAM_TARGET_CONTENT_PROPERTY, ContentModel.PROP_CONTENT); parametersUnderTest.put(RenditionService.PARAM_DESTINATION_NODE, dummyNodeRef2); + parametersUnderTest.put(PagedSourceOptionsSerializer.PARAM_SOURCE_START_PAGE, new Integer(2)); + parametersUnderTest.put(PagedSourceOptionsSerializer.PARAM_SOURCE_END_PAGE, new Integer(2)); + parametersUnderTest.put(TemporalSourceOptionsSerializer.PARAM_SOURCE_TIME_OFFSET, "00:00:00.5"); ImageTransformationOptions imageTransOpts = new ImageTransformationOptions(); @@ -151,6 +158,15 @@ public class ThumbnailServiceImplParameterTest resizeOptions.setAllowEnlargement((Boolean) parametersUnderTest.get(ImageRenderingEngine.PARAM_ALLOW_ENLARGEMENT)); imageTransOpts.setResizeOptions(resizeOptions); + PagedSourceOptions pagedSourceOptions = new PagedSourceOptions(); + pagedSourceOptions.setStartPageNumber((Integer) parametersUnderTest.get(PagedSourceOptionsSerializer.PARAM_SOURCE_START_PAGE)); + pagedSourceOptions.setEndPageNumber((Integer) parametersUnderTest.get(PagedSourceOptionsSerializer.PARAM_SOURCE_END_PAGE)); + imageTransOpts.addSourceOptions(pagedSourceOptions); + + TemporalSourceOptions temporalSourceOptions = new TemporalSourceOptions(); + temporalSourceOptions.setOffset((String) parametersUnderTest.get(TemporalSourceOptionsSerializer.PARAM_SOURCE_TIME_OFFSET)); + imageTransOpts.addSourceOptions(temporalSourceOptions); + ThumbnailParentAssociationDetails assocDetails = new ThumbnailParentAssociationDetails(dummyNodeRef3, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "homerSimpson")); diff --git a/source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java b/source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java index ec95256c2b..e090600780 100644 --- a/source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java +++ b/source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java @@ -49,6 +49,7 @@ import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentServiceTransientException; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.PagedSourceOptions; import org.alfresco.service.cmr.repository.ScriptLocation; import org.alfresco.service.cmr.repository.ScriptService; import org.alfresco.service.cmr.repository.TransformationOptions; @@ -175,6 +176,39 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest checkRendition("doclib", thumbnail0); outputThumbnailTempContentLocation(thumbnail0, "jpg", "doclib test"); } + + public void testCreateRenditionThumbnailFromPdfPage2() throws Exception + { + ImageTransformationOptions options = new ImageTransformationOptions(); + PagedSourceOptions pagedSourceOptions = new PagedSourceOptions(); + pagedSourceOptions.setStartPageNumber(new Integer(2)); + pagedSourceOptions.setEndPageNumber(new Integer(2)); + options.addSourceOptions(pagedSourceOptions); + + ThumbnailDefinition thumbnailDefinition = new ThumbnailDefinition(MimetypeMap.MIMETYPE_PDF, options, "doclib_2"); + thumbnailService.getThumbnailRegistry().addThumbnailDefinition(thumbnailDefinition); + + checkTransformer(); + + NodeRef pdfOrig = createOriginalContent(this.folder, MimetypeMap.MIMETYPE_PDF); + + NodeRef thumbnail0 = this.thumbnailService.createThumbnail(pdfOrig, ContentModel.PROP_CONTENT, + MimetypeMap.MIMETYPE_IMAGE_JPEG, thumbnailDefinition.getTransformationOptions(), "doclib_2"); + assertNotNull(thumbnail0); + checkRenditioned(pdfOrig, "doclib_2"); + checkRendition("doclib_2", thumbnail0); + + // Check the length + File tempFile = TempFileProvider.createTempFile("thumbnailServiceImplTest", ".jpg"); + ContentReader reader = this.contentService.getReader(thumbnail0, ContentModel.PROP_CONTENT); + + long size = reader.getSize(); + System.out.println("size=" + size); + assertTrue("Page 2 should be blank and less than 4500 bytes", size < 4500); + + reader.getContent(tempFile); + System.out.println("doclib_2 test: " + tempFile.getPath()); + } public void testCreateThumbnailFromImage() throws Exception { diff --git a/source/java/org/alfresco/service/cmr/repository/AbstractTransformationSourceOptions.java b/source/java/org/alfresco/service/cmr/repository/AbstractTransformationSourceOptions.java new file mode 100644 index 0000000000..7cd6d80bc4 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/repository/AbstractTransformationSourceOptions.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco 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 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Alfresco. If not, see . + */ +package org.alfresco.service.cmr.repository; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +/** + * Base implementation of TransformationSourceOptions which holds applicable mimetypes + * and handles merge of options. + * + * @author Ray Gauss II + */ +public abstract class AbstractTransformationSourceOptions implements TransformationSourceOptions, Cloneable +{ + protected static final String MIMETYPE_VIDEO_PREFIX = "video/"; + protected static final String MIMETYPE_AUDIO_PREFIX = "audio/"; + + /** The list of applicable mimetypes */ + private List applicabledMimetypes; + + /** + * Gets the list of applicable mimetypes + * + * @return the applicable mimetypes + */ + public List getApplicabledMimetypes() + { + return applicabledMimetypes; + } + + /** + * Sets the list of applicable mimetypes + * + * @param applicableMimetypes the applicable mimetypes + */ + public void setApplicableMimetypes(List applicabledMimetypes) + { + this.applicabledMimetypes = applicabledMimetypes; + } + + /** + * Gets whether or not these transformation source options apply for the + * given mimetype + * + * @param mimetype the mimetype of the source + * @return if these transformation source options apply + */ + public boolean isApplicableForMimetype(String mimetype) + { + if (mimetype != null && applicabledMimetypes != null) { return applicabledMimetypes.contains(mimetype); } + return false; + } + + @Override + protected AbstractTransformationSourceOptions clone() throws CloneNotSupportedException + { + return (AbstractTransformationSourceOptions) super.clone(); + } + + /** + * Creates a new TransformationSourceOptions object from this + * one, merging any non-null overriding fields in the given + * overridingOptions + * + * @param overridingOptions + * @return a merged TransformationSourceOptions object + */ + public TransformationSourceOptions mergedOptions(TransformationSourceOptions overridingOptions) + { + try + { + AbstractTransformationSourceOptions mergedOptions = this.clone(); + mergedOptions.setApplicableMimetypes(this.getApplicabledMimetypes()); + + return mergedOptions; + } + catch (CloneNotSupportedException e) + { + // Not thrown + } + return null; + } + + /** + * Adds the given paramValue to the given params if it's not null. + * + * @param paramName + * @param paramValue + * @param params + */ + protected void putParameterIfNotNull(String paramName, Serializable paramValue, Map params) + { + if (paramValue != null) + { + params.put(paramName, paramValue); + } + } + +} diff --git a/source/java/org/alfresco/service/cmr/repository/PagedSourceOptions.java b/source/java/org/alfresco/service/cmr/repository/PagedSourceOptions.java new file mode 100644 index 0000000000..ca48a47878 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/repository/PagedSourceOptions.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco 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 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Alfresco. If not, see . + */ +package org.alfresco.service.cmr.repository; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.service.cmr.repository.AbstractTransformationSourceOptions; + +/** + * Paged content conversion options to specify a page number range. + *

+ * The page numbering index starts with 1. + *

+ * If only the start page number is specified transformers should attempt + * a page range from that page number to the end if possible. + *

+ * If only an end page number is specified transformers should attempt + * a page range from the start to that page if possible. + * + * @author Ray Gauss II + */ +public class PagedSourceOptions extends AbstractTransformationSourceOptions +{ + public static final Integer PAGE_1 = new Integer(1); + + /** The start of the page range in the source document */ + private Integer startPageNumber; + + /** The end of the page range in the source document */ + private Integer endPageNumber; + + protected static List getDefaultApplicableMimetypes() + { + List defaults = new ArrayList(17); + defaults.add(MimetypeMap.MIMETYPE_PDF); + defaults.add(MimetypeMap.MIMETYPE_WORD); + defaults.add(MimetypeMap.MIMETYPE_PPT); + defaults.add(MimetypeMap.MIMETYPE_IMAGE_TIFF); + defaults.add(MimetypeMap.MIMETYPE_OPENDOCUMENT_PRESENTATION); + defaults.add(MimetypeMap.MIMETYPE_OPENDOCUMENT_PRESENTATION_TEMPLATE); + defaults.add(MimetypeMap.MIMETYPE_OPENDOCUMENT_TEXT_TEMPLATE); + defaults.add(MimetypeMap.MIMETYPE_OPENOFFICE1_WRITER); + defaults.add(MimetypeMap.MIMETYPE_OPENOFFICE1_IMPRESS); + defaults.add(MimetypeMap.MIMETYPE_OPENXML_PRESENTATION); + defaults.add(MimetypeMap.MIMETYPE_OPENXML_WORDPROCESSING); + defaults.add(MimetypeMap.MIMETYPE_STAROFFICE5_IMPRESS); + defaults.add(MimetypeMap.MIMETYPE_STAROFFICE5_IMPRESS_PACKED); + defaults.add(MimetypeMap.MIMETYPE_STAROFFICE5_WRITER); + defaults.add(MimetypeMap.MIMETYPE_STAROFFICE5_WRITER_GLOBAL); + defaults.add(MimetypeMap.MIMETYPE_IWORK_KEYNOTE); + defaults.add(MimetypeMap.MIMETYPE_IWORK_PAGES); + defaults.add(MimetypeMap.MIMETYPE_WORDPERFECT); + return defaults; + } + + public PagedSourceOptions() + { + super(); + setApplicableMimetypes(PagedSourceOptions.getDefaultApplicableMimetypes()); + } + + /** + * Gets the page number to start from in the source document + * + * @return the start page number + */ + public Integer getStartPageNumber() + { + return startPageNumber; + } + + /** + * Sets the page number to start from in the source document + * + * @param startPageNumber the start page number + */ + public void setStartPageNumber(Integer startPageNumber) + { + this.startPageNumber = startPageNumber; + } + + /** + * Gets the page number to end at in the source document + * + * @return the start page number + */ + public Integer getEndPageNumber() + { + return endPageNumber; + } + + /** + * Sets the page number to end at in the source document + * + * @param endPageNumber the end page number + */ + public void setEndPageNumber(Integer endPageNumber) + { + this.endPageNumber = endPageNumber; + } + + @Override + public TransformationSourceOptions mergedOptions(TransformationSourceOptions overridingOptions) + { + if (overridingOptions instanceof PagedSourceOptions) + { + PagedSourceOptions mergedOptions = (PagedSourceOptions) super.mergedOptions(overridingOptions); + + if (((PagedSourceOptions) overridingOptions).getStartPageNumber() != null) + { + mergedOptions.setStartPageNumber(((PagedSourceOptions) overridingOptions).getStartPageNumber()); + } + if (((PagedSourceOptions) overridingOptions).getEndPageNumber() != null) + { + mergedOptions.setEndPageNumber(((PagedSourceOptions) overridingOptions).getEndPageNumber()); + } + return mergedOptions; + } + return null; + } + + /** + * Gets paged source options which specify just the first page. + * + * @return the page one source options + */ + public static PagedSourceOptions getPage1Instance() { + PagedSourceOptions sourceOptions = new PagedSourceOptions(); + sourceOptions.setStartPageNumber(PAGE_1); + sourceOptions.setEndPageNumber(PAGE_1); + return sourceOptions; + } + + @Override + public TransformationSourceOptionsSerializer getSerializer() + { + return PagedSourceOptions.createSerializerInstance(); + } + + /** + * Creates an instance of the options serializer + * + * @return the options serializer + */ + public static TransformationSourceOptionsSerializer createSerializerInstance() + { + return (new PagedSourceOptions()).new PagedSourceOptionsSerializer(); + } + + /** + * Serializer for paged source options + */ + public class PagedSourceOptionsSerializer implements TransformationSourceOptionsSerializer + { + public static final String PARAM_SOURCE_START_PAGE = "source_start_page"; + public static final String PARAM_SOURCE_END_PAGE = "source_end_page"; + + @Override + public TransformationSourceOptions deserialize(SerializedTransformationOptionsAccessor serializedOptions) + { + int startPageNumber = serializedOptions.getIntegerParam(PARAM_SOURCE_START_PAGE, 1); + int endPageNumber = serializedOptions.getIntegerParam(PARAM_SOURCE_END_PAGE, 1); + + PagedSourceOptions sourceOptions = new PagedSourceOptions(); + sourceOptions.setStartPageNumber(startPageNumber); + sourceOptions.setEndPageNumber(endPageNumber); + return sourceOptions; + } + + @Override + public void serialize(TransformationSourceOptions sourceOptions, + Map parameters) + { + if (parameters == null || sourceOptions == null) + { + return; + } + PagedSourceOptions pagedSourceOptions = (PagedSourceOptions) sourceOptions; + parameters.put(PARAM_SOURCE_START_PAGE, pagedSourceOptions.getStartPageNumber()); + parameters.put(PARAM_SOURCE_END_PAGE, pagedSourceOptions.getEndPageNumber()); + } + } +} diff --git a/source/java/org/alfresco/service/cmr/repository/SerializedTransformationOptionsAccessor.java b/source/java/org/alfresco/service/cmr/repository/SerializedTransformationOptionsAccessor.java new file mode 100644 index 0000000000..4b16d24872 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/repository/SerializedTransformationOptionsAccessor.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco 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 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Alfresco. If not, see . + */ +package org.alfresco.service.cmr.repository; + +import org.alfresco.service.cmr.rendition.RenditionServiceException; + +/** + * Defines methods for retrieving parameter values for use in building + * transformation options. + * + * @author Ray Gauss II + */ +public interface SerializedTransformationOptionsAccessor +{ + + /** + * Gets the value for the named parameter. Checks the type of + * the parameter is correct and throws and Exception if it isn't. + * Returns null if the parameter value is null + * + * @param paramName the name of the parameter being checked. + * @param clazz the expected {@link Class} of the parameter value. + * @return the parameter value or null. + */ + public T getCheckedParam(String paramName, Class clazz); + + /** + * Gets the value for the named parameter. Checks the type of the + * parameter is the same as the type of defaultValue and + * throws a {@link RenditionServiceException} if it isn't. Returns + * defaultValue if the parameter value is null + * + * @param + * @param paramName + * @param defaultValue + * @return + */ + public T getParamWithDefault(String paramName, T defaultValue); + + /** + * Gets the int value for the named parameter. Returns + * defaultValue if the parameter value is null. + * + * @param key + * @param defaultValue + * @return + */ + public int getIntegerParam(String key, int defaultValue); + +} diff --git a/source/java/org/alfresco/service/cmr/repository/TemporalSourceOptions.java b/source/java/org/alfresco/service/cmr/repository/TemporalSourceOptions.java new file mode 100644 index 0000000000..2a419ae185 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/repository/TemporalSourceOptions.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco 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 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Alfresco. If not, see . + */ +package org.alfresco.service.cmr.repository; + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.service.cmr.repository.AbstractTransformationSourceOptions; + +/** + * Time-based content conversion options to specify an offset and duration. + * Useful for audio and video. + *

+ * If only the offset is specified transformers should attempt + * a transform from that offset to the end if possible. + *

+ * If only a duration is specified transformers should attempt + * a transform from the start until that duration is reached if possible. + * + * @author Ray Gauss II + */ +public class TemporalSourceOptions extends AbstractTransformationSourceOptions +{ + + /** The offset time code from which to start the transformation */ + private String offset; + + /** The duration of the target video after the transformation */ + private String duration; + + public boolean isApplicableForMimetype(String sourceMimetype) + { + return ((sourceMimetype != null && + sourceMimetype.startsWith(MIMETYPE_VIDEO_PREFIX) || + sourceMimetype.startsWith(MIMETYPE_AUDIO_PREFIX)) || + super.isApplicableForMimetype(sourceMimetype)); + } + + /** + * Gets the offset time code from which to start the transformation + * with a format of hh:mm:ss[.xxx] + * + * @return the offset + */ + public String getOffset() + { + return offset; + } + + /** + * Sets the offset time code from which to start the transformation + * with a format of hh:mm:ss[.xxx] + * + * @param offset + */ + public void setOffset(String offset) + { + this.offset = offset; + } + + /** + * Gets the duration of the source to read + * with a format of hh:mm:ss[.xxx] + * + * @return + */ + public String getDuration() + { + return duration; + } + + /** + * Sets the duration of the source to read + * with a format of hh:mm:ss[.xxx] + * + * @param duration + */ + public void setDuration(String duration) + { + this.duration = duration; + } + + @Override + public TransformationSourceOptions mergedOptions(TransformationSourceOptions overridingOptions) + { + if (overridingOptions instanceof TemporalSourceOptions) + { + TemporalSourceOptions mergedOptions = (TemporalSourceOptions) super.mergedOptions(overridingOptions); + + if (((TemporalSourceOptions) overridingOptions).getOffset() != null) + { + mergedOptions.setOffset(((TemporalSourceOptions) overridingOptions).getOffset()); + } + if (((TemporalSourceOptions) overridingOptions).getDuration() != null) + { + mergedOptions.setDuration(((TemporalSourceOptions) overridingOptions).getDuration()); + } + return mergedOptions; + } + return null; + } + + @Override + public TransformationSourceOptionsSerializer getSerializer() + { + return TemporalSourceOptions.createSerializerInstance(); + } + + /** + * Creates an instance of the options serializer + * + * @return the options serializer + */ + public static TransformationSourceOptionsSerializer createSerializerInstance() + { + return (new TemporalSourceOptions()).new TemporalSourceOptionsSerializer(); + } + + /** + * Serializer for temporal source options + */ + public class TemporalSourceOptionsSerializer implements TransformationSourceOptionsSerializer + { + public static final String PARAM_SOURCE_TIME_OFFSET = "source_time_offset"; + public static final String PARAM_SOURCE_TIME_DURATION = "source_time_duration"; + + @Override + public TransformationSourceOptions deserialize(SerializedTransformationOptionsAccessor serializedOptions) + { + String offset = serializedOptions.getCheckedParam(PARAM_SOURCE_TIME_OFFSET, String.class); + String duration = serializedOptions.getCheckedParam(PARAM_SOURCE_TIME_DURATION, String.class); + + if (offset == null && duration == null) + { + return null; + } + + TemporalSourceOptions sourceOptions = new TemporalSourceOptions(); + sourceOptions.setOffset(offset); + sourceOptions.setDuration(duration); + return sourceOptions; + } + + @Override + public void serialize(TransformationSourceOptions sourceOptions, + Map parameters) + { + if (parameters == null || sourceOptions == null) + return; + TemporalSourceOptions temporalSourceOptions = (TemporalSourceOptions) sourceOptions; + parameters.put(PARAM_SOURCE_TIME_OFFSET, temporalSourceOptions.getOffset()); + parameters.put(PARAM_SOURCE_TIME_DURATION, temporalSourceOptions.getDuration()); + } + } + + +} diff --git a/source/java/org/alfresco/service/cmr/repository/TransformationOptions.java b/source/java/org/alfresco/service/cmr/repository/TransformationOptions.java index 209bf55f15..7d7de3691c 100644 --- a/source/java/org/alfresco/service/cmr/repository/TransformationOptions.java +++ b/source/java/org/alfresco/service/cmr/repository/TransformationOptions.java @@ -18,11 +18,16 @@ */ package org.alfresco.service.cmr.repository; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.alfresco.service.cmr.repository.datatype.TypeConverter; import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** * Class containing values of options that are passed to content transformers. These options @@ -37,6 +42,8 @@ import org.alfresco.service.namespace.QName; */ public class TransformationOptions implements Cloneable { + private static final Log logger = LogFactory.getLog(TransformationOptions.class); + /** Option map names to preserve backward compatibility */ public static final String OPT_SOURCE_NODEREF = "contentReaderNodeRef"; public static final String OPT_SOURCE_CONTENT_PROPERTY = "sourceContentProperty"; @@ -61,6 +68,9 @@ public class TransformationOptions implements Cloneable /** Time, KBytes and page limits */ private TransformationOptionLimits limits = new TransformationOptionLimits(); + + /** Source options based on its mimetype */ + private Map, TransformationSourceOptions> sourceOptionsMap; /** * Default constructor @@ -124,6 +134,7 @@ public class TransformationOptions implements Cloneable public void copyFrom(TransformationOptions otherOptions) { this.set(otherOptions.toMap()); + this.setSourceOptionsList(otherOptions.getSourceOptionsList()); } /** @@ -419,7 +430,87 @@ public class TransformationOptions implements Cloneable { this.limits = limits; } - + + /** + * Gets the map of source options further describing how the source should + * be transformed based on its mimetype + * + * @return the source mimetype to source options map + */ + protected Map, TransformationSourceOptions> getSourceOptionsMap() + { + return sourceOptionsMap; + } + + /** + * Gets the immutable list of source options further describing how the source should + * be transformed based on its mimetype. + * Use {@link TransformationOptions#addSourceOptions(TransformationSourceOptions)} + * to add source options. + * + * @return the source options list + */ + public Collection getSourceOptionsList() + { + if (sourceOptionsMap == null) + return null; + return sourceOptionsMap.values(); + } + + /** + * Sets the list of source options further describing how the source should + * be transformed based on its mimetype. + * + * @param sourceOptionsList the source options list + */ + public void setSourceOptionsList(Collection sourceOptionsList) + { + if (sourceOptionsList != null) + { + for (TransformationSourceOptions sourceOptions : sourceOptionsList) + { + addSourceOptions(sourceOptions); + } + } + } + + /** + * Adds the given sourceOptions to the sourceOptionsMap. + *

+ * Note that if source options of the same class already exists a new + * merged source options object is added. + * + * @param sourceOptions + */ + public void addSourceOptions(TransformationSourceOptions sourceOptions) + { + if (sourceOptionsMap == null) + { + sourceOptionsMap = new HashMap, TransformationSourceOptions>(1); + } + TransformationSourceOptions newOptions = sourceOptions; + TransformationSourceOptions existingOptions = sourceOptionsMap.get(sourceOptions.getClass()); + if (existingOptions != null) + { + newOptions = existingOptions.mergedOptions(sourceOptions); + } + sourceOptionsMap.put(sourceOptions.getClass(), newOptions); + } + + /** + * Gets the appropriate source options for the given mimetype if available. + * + * @param sourceMimetype + * @return the source options for the mimetype + */ + @SuppressWarnings("unchecked") + public T getSourceOptions(Class clazz) + { + if (sourceOptionsMap == null) + return null; + return (T) sourceOptionsMap.get(clazz); + } + /** * Convert the transformation options into a map. *

diff --git a/source/java/org/alfresco/service/cmr/repository/TransformationSourceOptions.java b/source/java/org/alfresco/service/cmr/repository/TransformationSourceOptions.java new file mode 100644 index 0000000000..40f4e3af05 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/repository/TransformationSourceOptions.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco 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 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Alfresco. If not, see . + */ +package org.alfresco.service.cmr.repository; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.rendition.executer.AbstractRenderingEngine; +import org.alfresco.service.cmr.rendition.RenditionService; +import org.alfresco.service.cmr.repository.PagedSourceOptions; + +/** + * Defines options and demarcations needed to describe the details of how + * the source should be transformed, independent of the target requirements. + *

+ * See {@link PagedSourceOptions} for an example implementation that + * describes the page number that should be used from the source content. + * + * @author Ray Gauss II + */ +public interface TransformationSourceOptions +{ + + /** + * Gets the list of applicable mimetypes + * + * @return the applicable mimetypes + */ + public List getApplicabledMimetypes(); + + /** + * Gets whether or not these transformation source options apply for the + * given mimetype + * + * @param mimetype the mimetype of the source + * @return if these transformation source options apply + */ + public boolean isApplicableForMimetype(String mimetype); + + /** + * Creates a new TransformationSourceOptions object from this + * one, merging any non-null overriding fields in the given + * overridingOptions + * + * @param overridingOptions + * @return a merged TransformationSourceOptions object + */ + public TransformationSourceOptions mergedOptions(TransformationSourceOptions overridingOptions); + + /** + * Gets the serializer for the source options. + * + * @return the serializer + */ + public TransformationSourceOptionsSerializer getSerializer(); + + /** + * Defines methods for serializing the source options into a parameter map and + * deserializing from a serialized options accessor. + *

+ * This is primarily used when interacting with the {@link RenditionService} + * with {@link AbstractRenderingEngine}'s RenderContext being an implementer + * of this interface. + */ + public interface TransformationSourceOptionsSerializer + { + + /** + * Serializes the given transformation source options into the given parameter map. + * + * @param transformationSourceOptions + * @param parameters + */ + public void serialize(TransformationSourceOptions transformationSourceOptions, Map parameters); + + /** + * Gets the parameters from the serialized options accessor and builds a source options object. + * + * @param serializedOptions + * @return the deserialized source options + */ + public TransformationSourceOptions deserialize(SerializedTransformationOptionsAccessor serializedOptions); + + } + +} + +