Commits

Edi Weissmann committed b1cb848

re #52: added few missing short names for options, added unit test that checks short names are not clashing for one cli
re #51: added prefix option to all commands that have folder as output (except unpack), renamed simplesplit's predefinedPages option short name from -p to -s due to collision on short name with prefix. added unit test that tests the prefixable output trait
refactored folder output/file output trait tests to cover all testabl tasks and to derive the information on whether a task has folder output directly from the cli arguments class

Comments (0)

Files changed (33)

sejda-console/src/main/java/org/sejda/cli/model/CliArgumentsWithImageOutput.java

  */
 public interface CliArgumentsWithImageOutput extends TaskCliArguments {
 
-    @Option(description = "image color type: black_and_white, gray_scale, color_rgb")
+    @Option(shortName = "c", description = "image color type: { black_and_white, gray_scale, color_rgb } (required)")
     ImageColorTypeAdapter getColorType();
 
-    @Option(description = "resolution in dpi")
+    @Option(shortName = "r", description = "resolution in dpi. Default is 72 (optional)")
     int getResolution();
 
     boolean isResolution();

sejda-console/src/main/java/org/sejda/cli/model/CliArgumentsWithPrefixableOutput.java

+/*
+ * Created on Oct 11, 2011
+ * Copyright 2010 by Eduard Weissmann (edi.weissmann@gmail.com).
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0 
+ * 
+ * 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.sejda.cli.model;
+
+import uk.co.flamingpenguin.jewel.cli.Option;
+
+/**
+ * Cli arguments that support a specified prefix to be applied to the name of the outputs
+ * 
+ * @author Eduard Weissmann
+ * 
+ */
+public interface CliArgumentsWithPrefixableOutput {
+
+    @Option(shortName = "p", description = "prefix for the output files name (optional)", defaultValue = "")
+    String getOutputPrefix();
+}

sejda-console/src/main/java/org/sejda/cli/model/DecryptTaskCliArguments.java

  */
 package org.sejda.cli.model;
 
-
 import uk.co.flamingpenguin.jewel.cli.CommandLineInterface;
-import uk.co.flamingpenguin.jewel.cli.Option;
 
 /**
  * Specifications for command line options of the Decrypt task
  * 
  */
 @CommandLineInterface(application = TaskCliArguments.EXECUTABLE_NAME + " decrypt")
-public interface DecryptTaskCliArguments extends CliArgumentsWithPdfAndDirectoryOutput {
-    @Option(shortName = "p", description = "prefix for the output files name (optional)", defaultValue = "")
-    String getOutputPrefix();
+public interface DecryptTaskCliArguments extends CliArgumentsWithPdfAndDirectoryOutput,
+        CliArgumentsWithPrefixableOutput {
+
 }

sejda-console/src/main/java/org/sejda/cli/model/EncryptTaskCliArguments.java

  * 
  */
 @CommandLineInterface(application = TaskCliArguments.EXECUTABLE_NAME + " encrypt")
-public interface EncryptTaskCliArguments extends CliArgumentsWithPdfAndDirectoryOutput {
+public interface EncryptTaskCliArguments extends CliArgumentsWithPdfAndDirectoryOutput,
+        CliArgumentsWithPrefixableOutput {
 
     @Option(description = "permissions: a list of permissions. { print, modify, copy, modifyannotations, fill, screenreaders, assembly, degradedprinting}  (optional)")
     List<PdfAccessPermissionAdapter> getAllow();
 
     @Option(shortName = "e", description = "encryption angorithm {rc4_40, rc4_128, aes_128}. If omitted it uses rc4_128 (optional)", defaultValue = "rc4_128")
     PdfEncryptionAdapter getEncryptionType();
-
-    @Option(shortName = "p", description = "prefix for the output files name (optional)", defaultValue = "")
-    String getOutputPrefix();
 }

sejda-console/src/main/java/org/sejda/cli/model/ExtractTextTaskCliArguments.java

  */
 package org.sejda.cli.model;
 
-
 import uk.co.flamingpenguin.jewel.cli.CommandLineInterface;
 import uk.co.flamingpenguin.jewel.cli.Option;
 
  * 
  */
 @CommandLineInterface(application = TaskCliArguments.EXECUTABLE_NAME + " extracttext")
-public interface ExtractTextTaskCliArguments extends CliArgumentsWithPdfAndDirectoryOutput {
-
-    @Option(shortName = "p", description = "prefix for the output files name (optional)", defaultValue = "")
-    String getOutputPrefix();
+public interface ExtractTextTaskCliArguments extends CliArgumentsWithPdfAndDirectoryOutput,
+        CliArgumentsWithPrefixableOutput {
 
     @Option(shortName = "e", description = "text encoding, default is UTF-8 (optional)", defaultValue = "UTF-8")
     String getTextEncoding();

sejda-console/src/main/java/org/sejda/cli/model/PdfToMultipleTiffTaskCliArguments.java

  * 
  */
 @CommandLineInterface(application = TaskCliArguments.EXECUTABLE_NAME + " pdftomultipletiff")
-public interface PdfToMultipleTiffTaskCliArguments extends CliArgumentsWithImageAndDirectoryOutput {
+public interface PdfToMultipleTiffTaskCliArguments extends CliArgumentsWithImageAndDirectoryOutput,
+        CliArgumentsWithPrefixableOutput {
 
-    @Option(description = "image compression type: none, ccitt_group_3_1d, ccitt_group_3_2d, ccitt_group_4, lzw, jpeg_ttn2, packbits, deflate. Default is 'none'", defaultValue = "none")
+    @Option(shortName = "x", description = "image compression type: { none, ccitt_group_3_1d, ccitt_group_3_2d, ccitt_group_4, lzw, jpeg_ttn2, packbits, deflate. Default is 'none' } (optional)", defaultValue = "none")
     TiffCompressionTypeAdapter getCompressionType();
 
     // override default -f option that is described as expecting a list of files with a description stating that it is expecting a single file

sejda-console/src/main/java/org/sejda/cli/model/PdfToSingleTiffTaskCliArguments.java

 @CommandLineInterface(application = TaskCliArguments.EXECUTABLE_NAME + " pdftosingletiff")
 public interface PdfToSingleTiffTaskCliArguments extends CliArgumentsWithImageFileOutput {
 
-    @Option(description = "image compression type: none, ccitt_group_3_1d, ccitt_group_3_2d, ccitt_group_4, lzw, jpeg_ttn2, packbits, deflate. Default is 'none'", defaultValue = "NONE")
+    @Option(shortName = "x", description = "image compression type: {none, ccitt_group_3_1d, ccitt_group_3_2d, ccitt_group_4, lzw, jpeg_ttn2, packbits, deflate}. Default is 'none' (optional)", defaultValue = "NONE")
     TiffCompressionTypeAdapter getCompressionType();
 
     // override default -f option that is described as expecting a list of files with a description stating that it is expecting a single file

sejda-console/src/main/java/org/sejda/cli/model/RotateTaskCliArguments.java

  * 
  */
 @CommandLineInterface(application = TaskCliArguments.EXECUTABLE_NAME + " rotate")
-public interface RotateTaskCliArguments extends CliArgumentsWithPdfAndDirectoryOutput {
+public interface RotateTaskCliArguments extends CliArgumentsWithPdfAndDirectoryOutput, CliArgumentsWithPrefixableOutput {
 
     @Option(shortName = "r", description = "pages rotation. You can set pages rotation. Accepted string is "
             + "\"pages:rotationdegrees\" where pages can be one among 'all',"
             + "'odd', 'even' and where rotationdegrees can be '90', '180' or"
             + "'270'. Pages will be rotate clockwise (required)")
     PageRotationAdapter getPageRotation();
-
-    @Option(shortName = "p", description = "prefix for the output files name (optional)", defaultValue = "")
-    String getOutputPrefix();
 }

sejda-console/src/main/java/org/sejda/cli/model/SimpleSplitTaskCliArguments.java

  * 
  */
 @CommandLineInterface(application = TaskCliArguments.EXECUTABLE_NAME + " simplesplit")
-public interface SimpleSplitTaskCliArguments extends CliArgumentsWithPdfAndDirectoryOutput {
+public interface SimpleSplitTaskCliArguments extends CliArgumentsWithPdfAndDirectoryOutput,
+        CliArgumentsWithPrefixableOutput {
 
     // pdfsam-incompatibility no default, and this is part of a larger task: split
-    @Option(shortName = "p", description = "predefined pages mode. Accepted values are 'all', 'odd' or 'even' (required)")
+    @Option(shortName = "s", description = "predefined pages mode. Accepted values are 'all', 'odd' or 'even' (required)")
     PredefinedSetOfPagesAdapter getPredefinedPages();
 
     // override default -f option that is described as expecting a list of files with a description stating that it is expecting a single file

sejda-console/src/main/java/org/sejda/cli/model/SplitByBookmarksTaskCliArguments.java

  * 
  */
 @CommandLineInterface(application = TaskCliArguments.EXECUTABLE_NAME + " splitbybookmarks")
-public interface SplitByBookmarksTaskCliArguments extends CliArgumentsWithPdfAndDirectoryOutput {
+public interface SplitByBookmarksTaskCliArguments extends CliArgumentsWithPdfAndDirectoryOutput,
+        CliArgumentsWithPrefixableOutput {
 
     @Option(shortName = "l", description = "bookmarks depth to split at (required)")
     Integer getBookmarkLevel();

sejda-console/src/main/java/org/sejda/cli/model/SplitByPagesTaskCliArguments.java

  * 
  */
 @CommandLineInterface(application = TaskCliArguments.EXECUTABLE_NAME + " splitbypages")
-public interface SplitByPagesTaskCliArguments extends CliArgumentsWithPdfAndDirectoryOutput {
+public interface SplitByPagesTaskCliArguments extends CliArgumentsWithPdfAndDirectoryOutput,
+        CliArgumentsWithPrefixableOutput {
 
     @Option(shortName = "n", description = "page number(s) to split at (required)")
     List<Integer> getPageNumbers();

sejda-console/src/main/java/org/sejda/cli/model/SplitBySizeTaskCliArguments.java

  * 
  */
 @CommandLineInterface(application = TaskCliArguments.EXECUTABLE_NAME + " splitbysize")
-public interface SplitBySizeTaskCliArguments extends CliArgumentsWithPdfAndDirectoryOutput {
+public interface SplitBySizeTaskCliArguments extends CliArgumentsWithPdfAndDirectoryOutput,
+        CliArgumentsWithPrefixableOutput {
 
     @Option(shortName = "s", description = "size in bytes to split at (required)")
     Long getSize();

sejda-console/src/main/java/org/sejda/cli/model/ViewerPreferencesTaskCliArguments.java

  * 
  */
 @CommandLineInterface(application = TaskCliArguments.EXECUTABLE_NAME + " setviewerpreferences")
-public interface ViewerPreferencesTaskCliArguments extends CliArgumentsWithPdfAndDirectoryOutput {
+public interface ViewerPreferencesTaskCliArguments extends CliArgumentsWithPdfAndDirectoryOutput,
+        CliArgumentsWithPrefixableOutput {
     @Option(description = "center of the screen (optional)")
     boolean isCenterWindow();
 

sejda-console/src/main/java/org/sejda/cli/transformer/BaseCliArgumentsTransformer.java

 import org.sejda.cli.model.CliArgumentsWithPdfAndDirectoryOutput;
 import org.sejda.cli.model.CliArgumentsWithPdfFileOutput;
 import org.sejda.cli.model.CliArgumentsWithPdfOutput;
+import org.sejda.cli.model.CliArgumentsWithPrefixableOutput;
 import org.sejda.cli.model.TaskCliArguments;
 import org.sejda.cli.model.adapter.PdfFileSourceAdapter;
 import org.sejda.core.exception.SejdaRuntimeException;
 import org.sejda.core.manipulation.model.parameter.base.AbstractParameters;
 import org.sejda.core.manipulation.model.parameter.base.AbstractPdfOutputParameters;
+import org.sejda.core.manipulation.model.parameter.base.MultipleOutputTaskParameters;
 import org.sejda.core.manipulation.model.parameter.base.MultiplePdfSourceTaskParameters;
 import org.sejda.core.manipulation.model.parameter.base.SinglePdfSourceTaskParameters;
 import org.sejda.core.manipulation.model.parameter.image.AbstractPdfToImageParameters;
  */
 public class BaseCliArgumentsTransformer {
 
+    protected void populateOutputPrefix(MultipleOutputTaskParameters parameters,
+            CliArgumentsWithPrefixableOutput taskCliArguments) {
+        parameters.setOutputPrefix(taskCliArguments.getOutputPrefix());
+    }
+
     /**
      * Populates common parameters for a task with output pdf files into a directory
      * 

sejda-console/src/main/java/org/sejda/cli/transformer/CliCommand.java

 
 import org.apache.commons.lang.StringUtils;
 import org.sejda.cli.model.AlternateMixTaskCliArguments;
+import org.sejda.cli.model.CliArgumentsWithDirectoryOutput;
+import org.sejda.cli.model.CliArgumentsWithPrefixableOutput;
 import org.sejda.cli.model.CropTaskCliArguments;
 import org.sejda.cli.model.DecryptTaskCliArguments;
 import org.sejda.cli.model.EncryptTaskCliArguments;
         protected CommandCliArgumentsTransformer<SimpleSplitTaskCliArguments, SimpleSplitParameters> getArgumentsTransformer() {
             return new SimpleSplitCliArgumentsTransformer();
         }
-    }, "Splits a given pdf document at a predefined set of page numbers (all, odd pages, even pages).", "simplesplit -f /tmp/file1.pdf -o /tmp -p odd"),
+    }, "Splits a given pdf document at a predefined set of page numbers (all, odd pages, even pages).", "simplesplit -f /tmp/file1.pdf -o /tmp -s odd"),
     EXTRACT_PAGES("extractpages", new CliInterfacedTask<ExtractPagesTaskCliArguments, ExtractPagesParameters>() {
 
         @Override
 
         return result.toString();
     }
+
+    public boolean hasFolderOutput() {
+        return isInheritingTraitsFrom(CliArgumentsWithDirectoryOutput.class);
+    }
+
+    public boolean hasPrefixableOutput() {
+        return isInheritingTraitsFrom(CliArgumentsWithPrefixableOutput.class);
+    }
+
+    boolean isInheritingTraitsFrom(Class<?> parentClazz) {
+        return parentClazz.isAssignableFrom(getCliArgumentsClass());
+    }
+
+    public Class<?> getCliArgumentsClass() {
+        return cliInterfacedTask.getCliArgumentsClass();
+    }
 }
 
 /**
  */
 abstract class CliInterfacedTask<T extends TaskCliArguments, P extends TaskParameters> {
 
-    private Class<T> getCliArgumentsClass() {
+    @SuppressWarnings("unchecked")
+    protected Class<T> getCliArgumentsClass() {
         // returning T.class see http://www.artima.com/weblogs/viewpost.jsp?thread=208860
         ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
         return (Class<T>) parameterizedType.getActualTypeArguments()[0];

sejda-console/src/main/java/org/sejda/cli/transformer/DecryptCliArgumentsTransformer.java

         DecryptParameters parameters = new DecryptParameters();
         populateAbstractParameters(parameters, taskCliArguments);
         populateSourceParameters(parameters, taskCliArguments);
-        parameters.setOutputPrefix(taskCliArguments.getOutputPrefix());
+        populateOutputPrefix(parameters, taskCliArguments);
         return parameters;
     }
 

sejda-console/src/main/java/org/sejda/cli/transformer/EncryptCliArgumentsTransformer.java

         EncryptParameters parameters = new EncryptParameters(taskCliArguments.getEncryptionType().getEnumValue());
         populateAbstractParameters(parameters, taskCliArguments);
         populateSourceParameters(parameters, taskCliArguments);
-        parameters.setOutputPrefix(taskCliArguments.getOutputPrefix());
+        populateOutputPrefix(parameters, taskCliArguments);
+
         parameters.setOwnerPassword(taskCliArguments.getAdminstratorPassword());
         parameters.setUserPassword(taskCliArguments.getUserPassword());
         if (taskCliArguments.isAllow()) {

sejda-console/src/main/java/org/sejda/cli/transformer/ExtractTextCliArgumentsTransformer.java

     public ExtractTextParameters toTaskParameters(ExtractTextTaskCliArguments taskCliArguments) {
         final ExtractTextParameters parameters = new ExtractTextParameters(taskCliArguments.getOutput()
                 .getPdfDirectoryOutput());
-        parameters.setOutputPrefix(taskCliArguments.getOutputPrefix());
+        populateOutputPrefix(parameters, taskCliArguments);
+
         parameters.setOverwrite(taskCliArguments.getOverwrite());
         parameters.setTextEncoding(taskCliArguments.getTextEncoding());
 

sejda-console/src/main/java/org/sejda/cli/transformer/PdfToMultipleTiffCliArgumentsTransformer.java

 
         populateSourceParameters(parameters, taskCliArguments);
         populateAbstractParameters(parameters, taskCliArguments);
+        populateOutputPrefix(parameters, taskCliArguments);
 
         return parameters;
     }

sejda-console/src/main/java/org/sejda/cli/transformer/RotateCliArgumentsTransformer.java

         RotateParameters parameters = new RotateParameters(taskCliArguments.getPageRotation().getPageRotation());
         populateAbstractParameters(parameters, taskCliArguments);
         populateSourceParameters(parameters, taskCliArguments);
-        parameters.setOutputPrefix(taskCliArguments.getOutputPrefix());
+        populateOutputPrefix(parameters, taskCliArguments);
         return parameters;
     }
 }

sejda-console/src/main/java/org/sejda/cli/transformer/SimpleSplitCliArgumentsTransformer.java

 
         populateAbstractParameters(parameters, taskCliArguments);
         populateSourceParameters(parameters, taskCliArguments);
+        populateOutputPrefix(parameters, taskCliArguments);
 
         return parameters;
     }

sejda-console/src/main/java/org/sejda/cli/transformer/SplitByBookmarksCliArgumentsTransformer.java

 
         populateAbstractParameters(parameters, taskCliArguments);
         populateSourceParameters(parameters, taskCliArguments);
+        populateOutputPrefix(parameters, taskCliArguments);
 
         return parameters;
     }

sejda-console/src/main/java/org/sejda/cli/transformer/SplitByPagesCliArgumentsTransformer.java

 
         populateAbstractParameters(parameters, taskCliArguments);
         populateSourceParameters(parameters, taskCliArguments);
+        populateOutputPrefix(parameters, taskCliArguments);
 
         return parameters;
     }

sejda-console/src/main/java/org/sejda/cli/transformer/SplitBySizeCliArgumentsTransformer.java

 
         populateAbstractParameters(parameters, taskCliArguments);
         populateSourceParameters(parameters, taskCliArguments);
+        populateOutputPrefix(parameters, taskCliArguments);
 
         return parameters;
     }

sejda-console/src/main/java/org/sejda/cli/transformer/ViewerPreferencesCliArgumentsTransformer.java

         populateSourceParameters(parameters, taskCliArguments);
 
         populateActivePreferences(taskCliArguments, parameters);
+        populateOutputPrefix(parameters, taskCliArguments);
 
         parameters.setNfsMode(taskCliArguments.getNfsMode().getEnumValue());
 

sejda-console/src/test/java/org/sejda/cli/AbstractTaskTraitTest.java

  */
 package org.sejda.cli;
 
+import java.util.ArrayList;
+import java.util.Collection;
+
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
     public AbstractTaskTraitTest(TestableTask testableTask) {
         super(testableTask);
     }
+
+    public static Collection<Object[]> asData(TestableTask[] tasks) {
+        Collection<Object[]> result = new ArrayList<Object[]>();
+        for (TestableTask eachTask : tasks) {
+            result.add(new Object[] { eachTask });
+        }
+
+        return result;
+    }
 }

sejda-console/src/test/java/org/sejda/cli/FileOutputTraitTest.java

 import static org.junit.Assert.assertFalse;
 
 import java.io.File;
-import java.util.Arrays;
 import java.util.Collection;
 
 import org.junit.Test;
 
     @Parameters
     public static Collection<Object[]> data() {
-        return Arrays.asList(new Object[][] { { TestableTask.ALTERNATE_MIX }, { TestableTask.MERGE },
-                { TestableTask.EXTRACT_PAGES } });
+        return TestableTask.allTasksExceptFor(TestableTask.getTasksWithFolderOutput());
     }
 
     public FileOutputTraitTest(TestableTask testableTask) {

sejda-console/src/test/java/org/sejda/cli/FolderOutputTraitTest.java

 package org.sejda.cli;
 
 import java.io.File;
-import java.util.Arrays;
 import java.util.Collection;
 
 import org.junit.Test;
 
     @Parameters
     public static Collection<Object[]> data() {
-        return Arrays.asList(new Object[][] { { TestableTask.DECRYPT }, { TestableTask.ENCRYPT },
-                { TestableTask.ROTATE }, { TestableTask.SET_VIEWER_PREFERENCES }, { TestableTask.UNPACK },
-                { TestableTask.EXTRACT_TEXT } });
+        return asData(TestableTask.getTasksWithFolderOutput());
     }
 
     public FolderOutputTraitTest(TestableTask testableTask) {

sejda-console/src/test/java/org/sejda/cli/MultipleInputSourceFilesTraitTest.java

 package org.sejda.cli;
 
 import java.io.File;
-import java.util.ArrayList;
 import java.util.Collection;
 
 import org.junit.Test;
 
     @Parameters
     public static Collection<Object[]> data() {
-        Collection<Object[]> result = new ArrayList<Object[]>();
-        for (TestableTask eachTask : TestableTask.getTasksWithMultipleSouceFiles()) {
-            result.add(new Object[] { eachTask });
-        }
-
-        return result;
+        return asData(TestableTask.getTasksWithMultipleSouceFiles());
     }
 
     public MultipleInputSourceFilesTraitTest(TestableTask testableTask) {

sejda-console/src/test/java/org/sejda/cli/PrefixableOutputTraitTest.java

+/*
+ * Created on Aug 25, 2011
+ * Copyright 2010 by Eduard Weissmann (edi.weissmann@gmail.com).
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0 
+ * 
+ * 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.sejda.cli;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Collection;
+
+import org.junit.Test;
+import org.junit.runners.Parameterized.Parameters;
+import org.sejda.core.manipulation.model.parameter.base.MultipleOutputTaskParameters;
+
+/**
+ * For tasks that support a folder as output, test various scenarios related to this trait
+ * 
+ * @author Eduard Weissmann
+ * 
+ */
+public class PrefixableOutputTraitTest extends AbstractTaskTraitTest {
+
+    @Parameters
+    public static Collection<Object[]> data() {
+        return asData(TestableTask.getTasksWithPrefixableOutput());
+    }
+
+    public PrefixableOutputTraitTest(TestableTask testableTask) {
+        super(testableTask);
+    }
+
+    @Test
+    public void testOutputPrefix_Specified() {
+        MultipleOutputTaskParameters parameters = defaultCommandLine().with("-p", "fooPrefix").invokeSejdaConsole();
+        assertEquals("fooPrefix", parameters.getOutputPrefix());
+    }
+
+    @Test
+    public void testOutputPrefix_Default() {
+        MultipleOutputTaskParameters parameters = defaultCommandLine().invokeSejdaConsole();
+        assertEquals("", parameters.getOutputPrefix());
+    }
+}

sejda-console/src/test/java/org/sejda/cli/ShortNamesUniqueTraitTest.java

+/*
+ * Created on Oct 11, 2011
+ * Copyright 2010 by Eduard Weissmann (edi.weissmann@gmail.com).
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0 
+ * 
+ * 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.sejda.cli;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang.StringUtils;
+import org.junit.Test;
+import org.sejda.core.exception.SejdaRuntimeException;
+
+import uk.co.flamingpenguin.jewel.cli.Option;
+
+/**
+ * 
+ * Test verifying that short names are not repeated for each task cli interface
+ * 
+ * @author Eduard Weissmann
+ * 
+ */
+// TODO: add a similar test that verifies that all options have (optional) or (required) in the description
+public class ShortNamesUniqueTraitTest extends AcrossAllTasksTraitTest {
+
+    public ShortNamesUniqueTraitTest(TestableTask testableTask) {
+        super(testableTask);
+    }
+
+    @Test
+    public void testShortNamesAreUnique() {
+        Class<?> cliCommandClass = testableTask.getCorrespondingCliCommand().getCliArgumentsClass();
+        Map<String, Method> shortNamesMapping = new HashMap<String, Method>();
+
+        for (Method eachMethod : cliCommandClass.getMethods()) {
+            final Option optionAnnotation = eachMethod.getAnnotation(Option.class);
+
+            if (optionAnnotation == null) {
+                continue;
+            }
+
+            final String[] shortNames = optionAnnotation.shortName();
+
+            for (String eachShortName : shortNames) {
+                if (StringUtils.isBlank(eachShortName)) {
+                    continue;
+                }
+                if (shortNamesMapping.containsKey(eachShortName)) {
+                    throw new SejdaRuntimeException(getTaskName() + " has duplicate short names: '" + eachShortName
+                            + "' defined on " + eachMethod.getName() + "() and on "
+                            + shortNamesMapping.get(eachShortName).getName() + "()");
+                }
+                shortNamesMapping.put(eachShortName, eachMethod);
+            }
+        }
+    }
+}

sejda-console/src/test/java/org/sejda/cli/SimpleSplitTaskTest.java

 
     @Test
     public void predefinedPages_ALL_PAGES() {
-        SimpleSplitParameters parameters = defaultCommandLine().with("-p", "all").invokeSejdaConsole();
+        SimpleSplitParameters parameters = defaultCommandLine().with("-s", "all").invokeSejdaConsole();
         assertContainsAll(Arrays.asList(1, 2, 3, 4, 5), parameters.getPages(5));
     }
 
     @Test
     public void predefinedPages_ODD_PAGES() {
-        SimpleSplitParameters parameters = defaultCommandLine().with("-p", "odd").invokeSejdaConsole();
+        SimpleSplitParameters parameters = defaultCommandLine().with("-s", "odd").invokeSejdaConsole();
         assertContainsAll(Arrays.asList(1, 3, 5), parameters.getPages(5));
     }
 
     @Test
     public void predefinedPages_EVEN_PAGES() {
-        SimpleSplitParameters parameters = defaultCommandLine().with("-p", "even").invokeSejdaConsole();
+        SimpleSplitParameters parameters = defaultCommandLine().with("-s", "even").invokeSejdaConsole();
         assertContainsAll(Arrays.asList(2, 4), parameters.getPages(5));
     }
 
     @Test
     public void mandatoryParams() {
-        defaultCommandLine().without("-p").assertConsoleOutputContains("Option is mandatory: --predefinedPages");
+        defaultCommandLine().without("-s").assertConsoleOutputContains("Option is mandatory: --predefinedPages");
     }
 }

sejda-console/src/test/java/org/sejda/cli/TestableTask.java

         return allTasksExceptFor();
     }
 
+    // TODO: return array and use asData() to convert to list of object[]
     public static List<Object[]> allTasksExceptFor(TestableTask... exceptFor) {
         Collection<TestableTask> exceptForCollection = Arrays.asList(exceptFor);
         List<Object[]> result = new ArrayList<Object[]>();
                 TestableTask.SET_VIEWER_PREFERENCES, TestableTask.UNPACK, TestableTask.EXTRACT_TEXT,
                 TestableTask.ALTERNATE_MIX, TestableTask.MERGE };
     }
+
+    boolean hasFolderOutput() {
+        return getCorrespondingCliCommand().hasFolderOutput();
+    }
+
+    boolean hasPrefixableOutput() {
+        return getCorrespondingCliCommand().hasPrefixableOutput();
+    }
+
+    public static TestableTask[] getTasksWithFolderOutput() {
+        List<TestableTask> result = new ArrayList<TestableTask>();
+        for (TestableTask each : TestableTask.values()) {
+            if (each.hasFolderOutput()) {
+                result.add(each);
+            }
+        }
+        return result.toArray(new TestableTask[result.size()]);
+    }
+
+    public static TestableTask[] getTasksWithPrefixableOutput() {
+        List<TestableTask> result = new ArrayList<TestableTask>();
+        for (TestableTask each : TestableTask.values()) {
+            if (each.hasPrefixableOutput()) {
+                result.add(each);
+            }
+        }
+        return result.toArray(new TestableTask[result.size()]);
+    }
 }
 
 interface DefaultsProvider {
 class SimpleSplitDefaultsProvider extends SingleInputAndFolderOutputDefaultsProvider {
     @Override
     public CommandLineTestBuilder provideDefaults(String taskName) {
-        return super.provideDefaults(taskName).with("-p", "all");
+        return super.provideDefaults(taskName).with("-s", "all");
     }
 }