PackingOptions.java

  1. /*
  2.  *  Licensed to the Apache Software Foundation (ASF) under one or more
  3.  *  contributor license agreements.  See the NOTICE file distributed with
  4.  *  this work for additional information regarding copyright ownership.
  5.  *  The ASF licenses this file to You under the Apache License, Version 2.0
  6.  *  (the "License"); you may not use this file except in compliance with
  7.  *  the License.  You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  *  Unless required by applicable law or agreed to in writing, software
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  *  See the License for the specific language governing permissions and
  15.  *  limitations under the License.
  16.  */
  17. package org.apache.commons.compress.harmony.pack200;

  18. import java.nio.file.FileSystems;
  19. import java.util.ArrayList;
  20. import java.util.HashMap;
  21. import java.util.List;
  22. import java.util.Map;
  23. import java.util.Map.Entry;

  24. import org.objectweb.asm.Attribute;

  25. /**
  26.  * Manages the various options available for pack200.
  27.  */
  28. public class PackingOptions {

  29.     private static final Attribute[] EMPTY_ATTRIBUTE_ARRAY = {};
  30.     public static final long SEGMENT_LIMIT = 1_000_000L;
  31.     public static final String STRIP = "strip";
  32.     public static final String ERROR = "error";
  33.     public static final String PASS = "pass";
  34.     public static final String KEEP = "keep";

  35.     // All options are initially set to their defaults
  36.     private boolean gzip = true;
  37.     private boolean stripDebug;
  38.     private boolean keepFileOrder = true;
  39.     private long segmentLimit = SEGMENT_LIMIT;
  40.     private int effort = 5;
  41.     private String deflateHint = KEEP;
  42.     private String modificationTime = KEEP;
  43.     private final List<String> passFiles = new ArrayList<>();
  44.     private String unknownAttributeAction = PASS;
  45.     private final Map<String, String> classAttributeActions = new HashMap<>();
  46.     private final Map<String, String> fieldAttributeActions = new HashMap<>();
  47.     private final Map<String, String> methodAttributeActions = new HashMap<>();
  48.     private final Map<String, String> codeAttributeActions = new HashMap<>();
  49.     private boolean verbose;
  50.     private String logFile;

  51.     private Attribute[] unknownAttributeTypes;

  52.     public void addClassAttributeAction(final String attributeName, final String action) {
  53.         classAttributeActions.put(attributeName, action);
  54.     }

  55.     public void addCodeAttributeAction(final String attributeName, final String action) {
  56.         codeAttributeActions.put(attributeName, action);
  57.     }

  58.     public void addFieldAttributeAction(final String attributeName, final String action) {
  59.         fieldAttributeActions.put(attributeName, action);
  60.     }

  61.     public void addMethodAttributeAction(final String attributeName, final String action) {
  62.         methodAttributeActions.put(attributeName, action);
  63.     }

  64.     private void addOrUpdateAttributeActions(final List<Attribute> prototypes, final Map<String, String> attributeActions, final int tag) {
  65.         if (attributeActions != null && attributeActions.size() > 0) {
  66.             NewAttribute newAttribute;
  67.             for (final Entry<String, String> entry : attributeActions.entrySet()) {
  68.                 final String name = entry.getKey();
  69.                 final String action = entry.getValue();
  70.                 boolean prototypeExists = false;
  71.                 for (final Object prototype : prototypes) {
  72.                     newAttribute = (NewAttribute) prototype;
  73.                     if (newAttribute.type.equals(name)) {
  74.                         // if the attribute exists, update its context
  75.                         newAttribute.addContext(tag);
  76.                         prototypeExists = true;
  77.                         break;
  78.                     }
  79.                 }
  80.                 // if no attribute is found, add a new attribute
  81.                 if (!prototypeExists) {
  82.                     if (ERROR.equals(action)) {
  83.                         newAttribute = new NewAttribute.ErrorAttribute(name, tag);
  84.                     } else if (STRIP.equals(action)) {
  85.                         newAttribute = new NewAttribute.StripAttribute(name, tag);
  86.                     } else if (PASS.equals(action)) {
  87.                         newAttribute = new NewAttribute.PassAttribute(name, tag);
  88.                     } else {
  89.                         newAttribute = new NewAttribute(name, action, tag);
  90.                     }
  91.                     prototypes.add(newAttribute);
  92.                 }
  93.             }
  94.         }
  95.     }

  96.     /**
  97.      * Tell the compressor to pass the file with the given name, or if the name is a directory name all files under that directory will be passed.
  98.      *
  99.      * @param passFileName the file name
  100.      */
  101.     public void addPassFile(final String passFileName) {
  102.         String fileSeparator = FileSystems.getDefault().getSeparator();
  103.         if (fileSeparator.equals("\\")) {
  104.             // Need to escape backslashes for replaceAll(), which uses regex
  105.             fileSeparator += "\\";
  106.         }
  107.         passFiles.add(passFileName.replaceAll(fileSeparator, "/"));
  108.     }

  109.     public String getDeflateHint() {
  110.         return deflateHint;
  111.     }

  112.     public int getEffort() {
  113.         return effort;
  114.     }

  115.     public String getLogFile() {
  116.         return logFile;
  117.     }

  118.     public String getModificationTime() {
  119.         return modificationTime;
  120.     }

  121.     private String getOrDefault(final Map<String, String> map, final String type, final String defaultValue) {
  122.         return map == null ? defaultValue : map.getOrDefault(type, defaultValue);
  123.     }

  124.     public long getSegmentLimit() {
  125.         return segmentLimit;
  126.     }

  127.     public String getUnknownAttributeAction() {
  128.         return unknownAttributeAction;
  129.     }

  130.     public Attribute[] getUnknownAttributePrototypes() {
  131.         if (unknownAttributeTypes == null) {
  132.             final List<Attribute> prototypes = new ArrayList<>();
  133.             addOrUpdateAttributeActions(prototypes, classAttributeActions, AttributeDefinitionBands.CONTEXT_CLASS);
  134.             addOrUpdateAttributeActions(prototypes, methodAttributeActions, AttributeDefinitionBands.CONTEXT_METHOD);
  135.             addOrUpdateAttributeActions(prototypes, fieldAttributeActions, AttributeDefinitionBands.CONTEXT_FIELD);
  136.             addOrUpdateAttributeActions(prototypes, codeAttributeActions, AttributeDefinitionBands.CONTEXT_CODE);
  137.             unknownAttributeTypes = prototypes.toArray(EMPTY_ATTRIBUTE_ARRAY);
  138.         }
  139.         return unknownAttributeTypes;
  140.     }

  141.     public String getUnknownClassAttributeAction(final String type) {
  142.         return getOrDefault(classAttributeActions, type, unknownAttributeAction);
  143.     }

  144.     public String getUnknownCodeAttributeAction(final String type) {
  145.         return getOrDefault(codeAttributeActions, type, unknownAttributeAction);
  146.     }

  147.     public String getUnknownFieldAttributeAction(final String type) {
  148.         return getOrDefault(fieldAttributeActions, type, unknownAttributeAction);
  149.     }

  150.     public String getUnknownMethodAttributeAction(final String type) {
  151.         return getOrDefault(methodAttributeActions, type, unknownAttributeAction);
  152.     }

  153.     public boolean isGzip() {
  154.         return gzip;
  155.     }

  156.     public boolean isKeepDeflateHint() {
  157.         return KEEP.equals(deflateHint);
  158.     }

  159.     public boolean isKeepFileOrder() {
  160.         return keepFileOrder;
  161.     }

  162.     public boolean isPassFile(final String passFileName) {
  163.         for (String pass : passFiles) {
  164.             if (passFileName.equals(pass)) {
  165.                 return true;
  166.             }
  167.             if (!pass.endsWith(".class")) { // a whole directory is
  168.                 // passed
  169.                 if (!pass.endsWith("/")) {
  170.                     // Make sure we don't get any false positives (e.g.
  171.                     // exclude "org/apache/harmony/pack" should not match
  172.                     // files under "org/apache/harmony/pack200/")
  173.                     pass += "/";
  174.                 }
  175.                 return passFileName.startsWith(pass);
  176.             }
  177.         }
  178.         return false;
  179.     }

  180.     public boolean isStripDebug() {
  181.         return stripDebug;
  182.     }

  183.     public boolean isVerbose() {
  184.         return verbose;
  185.     }

  186.     public void removePassFile(final String passFileName) {
  187.         passFiles.remove(passFileName);
  188.     }

  189.     public void setDeflateHint(final String deflateHint) {
  190.         if (!KEEP.equals(deflateHint) && !"true".equals(deflateHint) && !"false".equals(deflateHint)) {
  191.             throw new IllegalArgumentException("Bad argument: -H " + deflateHint + " ? deflate hint should be either true, false or keep (default)");
  192.         }
  193.         this.deflateHint = deflateHint;
  194.     }

  195.     /**
  196.      * Sets the compression effort level (0-9, equivalent to -E command line option)
  197.      *
  198.      * @param effort the compression effort level, 0-9.
  199.      */
  200.     public void setEffort(final int effort) {
  201.         this.effort = effort;
  202.     }

  203.     public void setGzip(final boolean gzip) {
  204.         this.gzip = gzip;
  205.     }

  206.     public void setKeepFileOrder(final boolean keepFileOrder) {
  207.         this.keepFileOrder = keepFileOrder;
  208.     }

  209.     public void setLogFile(final String logFile) {
  210.         this.logFile = logFile;
  211.     }

  212.     public void setModificationTime(final String modificationTime) {
  213.         if (!KEEP.equals(modificationTime) && !"latest".equals(modificationTime)) {
  214.             throw new IllegalArgumentException("Bad argument: -m " + modificationTime + " ? transmit modtimes should be either latest or keep (default)");
  215.         }
  216.         this.modificationTime = modificationTime;
  217.     }

  218.     public void setQuiet(final boolean quiet) {
  219.         this.verbose = !quiet;
  220.     }

  221.     /**
  222.      * Sets the segment limit (equivalent to -S command line option)
  223.      *
  224.      * @param segmentLimit   the limit in bytes
  225.      */
  226.     public void setSegmentLimit(final long segmentLimit) {
  227.         this.segmentLimit = segmentLimit;
  228.     }

  229.     /**
  230.      * Sets strip debug attributes. If true, all debug attributes (i.e. LineNumberTable, SourceFile, LocalVariableTable and LocalVariableTypeTable attributes)
  231.      * are stripped when reading the input class files and not included in the output archive.
  232.      *
  233.      * @param stripDebug If true, all debug attributes.
  234.      */
  235.     public void setStripDebug(final boolean stripDebug) {
  236.         this.stripDebug = stripDebug;
  237.     }

  238.     /**
  239.      * Sets the compressor behavior when an unknown attribute is encountered.
  240.      *
  241.      * @param unknownAttributeAction   the action to perform
  242.      */
  243.     public void setUnknownAttributeAction(final String unknownAttributeAction) {
  244.         this.unknownAttributeAction = unknownAttributeAction;
  245.         if (!PASS.equals(unknownAttributeAction) && !ERROR.equals(unknownAttributeAction) && !STRIP.equals(unknownAttributeAction)) {
  246.             throw new IllegalArgumentException("Incorrect option for -U, " + unknownAttributeAction);
  247.         }
  248.     }

  249.     public void setVerbose(final boolean verbose) {
  250.         this.verbose = verbose;
  251.     }

  252. }