    001/*
    002 *  Licensed to the Apache Software Foundation (ASF) under one or more
    003 *  contributor license agreements.  See the NOTICE file distributed with
    004 *  this work for additional information regarding copyright ownership.
    005 *  The ASF licenses this file to You under the Apache License, Version 2.0
    006 *  (the "License"); you may not use this file except in compliance with
    007 *  the License.  You may obtain a copy of the License at
    008 *
    009 *     http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 *  Unless required by applicable law or agreed to in writing, software
    012 *  distributed under the License is distributed on an "AS IS" BASIS,
    013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 *  See the License for the specific language governing permissions and
    015 *  limitations under the License.
    016 */
    017package org.apache.commons.compress.harmony.pack200;
    018
    019import java.util.ArrayList;
    020import java.util.HashMap;
    021import java.util.Iterator;
    022import java.util.List;
    023import java.util.Map;
    024
    025import org.objectweb.asm.Attribute;
    026
    027/**
    028 * Utility class to manage the various options available for pack200
    029 */
    030public class PackingOptions {
    031
    032    public static final String STRIP = "strip";
    033    public static final String ERROR = "error";
    034    public static final String PASS = "pass";
    035    public static final String KEEP = "keep";
    036
    037    // All options are initially set to their defaults
    038    private boolean gzip = true;
    039    private boolean stripDebug = false;
    040    private boolean keepFileOrder = true;
    041    private long segmentLimit = 1000000L;
    042    private int effort = 5;
    043    private String deflateHint = KEEP;
    044    private String modificationTime = KEEP;
    045    private List passFiles;
    046    private String unknownAttributeAction = PASS;
    047    private Map classAttributeActions;
    048    private Map fieldAttributeActions;
    049    private Map methodAttributeActions;
    050    private Map codeAttributeActions;
    051    private boolean verbose = false;
    052    private String logFile;
    053
    054    private Attribute[] unknownAttributeTypes;
    055
    056    public boolean isGzip() {
    057        return gzip;
    058    }
    059
    060    public void setGzip(final boolean gzip) {
    061        this.gzip = gzip;
    062    }
    063
    064    public boolean isStripDebug() {
    065        return stripDebug;
    066    }
    067
    068    /**
    069     * Set strip debug attributes. If true, all debug attributes (i.e. LineNumberTable, SourceFile, LocalVariableTable
    070     * and LocalVariableTypeTable attributes) are stripped when reading the input class files and not included in the
    071     * output archive.
    072     *
    073     * @param stripDebug If true, all debug attributes.
    074     */
    075    public void setStripDebug(final boolean stripDebug) {
    076        this.stripDebug = stripDebug;
    077    }
    078
    079    public boolean isKeepFileOrder() {
    080        return keepFileOrder;
    081    }
    082
    083    public void setKeepFileOrder(final boolean keepFileOrder) {
    084        this.keepFileOrder = keepFileOrder;
    085    }
    086
    087    public long getSegmentLimit() {
    088        return segmentLimit;
    089    }
    090
    091    /**
    092     * Set the segment limit (equivalent to -S command line option)
    093     *
    094     * @param segmentLimit - the limit in bytes
    095     */
    096    public void setSegmentLimit(final long segmentLimit) {
    097        this.segmentLimit = segmentLimit;
    098    }
    099
    100    public int getEffort() {
    101        return effort;
    102    }
    103
    104    /**
    105     * Sets the compression effort level (0-9, equivalent to -E command line option)
    106     *
    107     * @param effort the compression effort level, 0-9.
    108     */
    109    public void setEffort(final int effort) {
    110        this.effort = effort;
    111    }
    112
    113    public String getDeflateHint() {
    114        return deflateHint;
    115    }
    116
    117    public boolean isKeepDeflateHint() {
    118        return KEEP.equals(deflateHint);
    119    }
    120
    121    public void setDeflateHint(final String deflateHint) {
    122        if (!KEEP.equals(deflateHint) && !"true".equals(deflateHint) && !"false".equals(deflateHint)) {
    123            throw new IllegalArgumentException(
    124                "Bad argument: -H " + deflateHint + " ? deflate hint should be either true, false or keep (default)");
    125        }
    126        this.deflateHint = deflateHint;
    127    }
    128
    129    public String getModificationTime() {
    130        return modificationTime;
    131    }
    132
    133    public void setModificationTime(final String modificationTime) {
    134        if (!KEEP.equals(modificationTime) && !"latest".equals(modificationTime)) {
    135            throw new IllegalArgumentException("Bad argument: -m " + modificationTime
    136                + " ? transmit modtimes should be either latest or keep (default)");
    137        }
    138        this.modificationTime = modificationTime;
    139    }
    140
    141    public boolean isPassFile(final String passFileName) {
    142        if (passFiles != null) {
    143            for (final Iterator iterator = passFiles.iterator(); iterator.hasNext();) {
    144                String pass = (String) iterator.next();
    145                if (passFileName.equals(pass)) {
    146                    return true;
    147                }
    148                if (!pass.endsWith(".class")) { // a whole directory is
    149                    // passed
    150                    if (!pass.endsWith("/")) {
    151                        // Make sure we don't get any false positives (e.g.
    152                        // exclude "org/apache/harmony/pack" should not match
    153                        // files under "org/apache/harmony/pack200/")
    154                        pass = pass + "/";
    155                    }
    156                    return passFileName.startsWith(pass);
    157                }
    158            }
    159        }
    160        return false;
    161    }
    162
    163    /**
    164     * Tell the compressor to pass the file with the given name, or if the name is a directory name all files under that
    165     * directory will be passed.
    166     *
    167     * @param passFileName the file name
    168     */
    169    public void addPassFile(String passFileName) {
    170        if (passFiles == null) {
    171            passFiles = new ArrayList();
    172        }
    173        String fileSeparator = System.getProperty("file.separator");
    174        if (fileSeparator.equals("\\")) {
    175            // Need to escape backslashes for replaceAll(), which uses regex
    176            fileSeparator += "\\";
    177        }
    178        passFileName = passFileName.replaceAll(fileSeparator, "/");
    179        passFiles.add(passFileName);
    180    }
    181
    182    public void removePassFile(final String passFileName) {
    183        passFiles.remove(passFileName);
    184    }
    185
    186    public String getUnknownAttributeAction() {
    187        return unknownAttributeAction;
    188    }
    189
    190    /**
    191     * Tell the compressor what to do if an unknown attribute is encountered
    192     *
    193     * @param unknownAttributeAction - the action to perform
    194     */
    195    public void setUnknownAttributeAction(final String unknownAttributeAction) {
    196        this.unknownAttributeAction = unknownAttributeAction;
    197        if (!PASS.equals(unknownAttributeAction) && !ERROR.equals(unknownAttributeAction)
    198            && !STRIP.equals(unknownAttributeAction)) {
    199            throw new RuntimeException("Incorrect option for -U, " + unknownAttributeAction);
    200        }
    201    }
    202
    203    public void addClassAttributeAction(final String attributeName, final String action) {
    204        if (classAttributeActions == null) {
    205            classAttributeActions = new HashMap();
    206        }
    207        classAttributeActions.put(attributeName, action);
    208    }
    209
    210    public void addFieldAttributeAction(final String attributeName, final String action) {
    211        if (fieldAttributeActions == null) {
    212            fieldAttributeActions = new HashMap();
    213        }
    214        fieldAttributeActions.put(attributeName, action);
    215    }
    216
    217    public void addMethodAttributeAction(final String attributeName, final String action) {
    218        if (methodAttributeActions == null) {
    219            methodAttributeActions = new HashMap();
    220        }
    221        methodAttributeActions.put(attributeName, action);
    222    }
    223
    224    public void addCodeAttributeAction(final String attributeName, final String action) {
    225        if (codeAttributeActions == null) {
    226            codeAttributeActions = new HashMap();
    227        }
    228        codeAttributeActions.put(attributeName, action);
    229    }
    230
    231    public boolean isVerbose() {
    232        return verbose;
    233    }
    234
    235    public void setVerbose(final boolean verbose) {
    236        this.verbose = verbose;
    237    }
    238
    239    public void setQuiet(final boolean quiet) {
    240        this.verbose = !quiet;
    241    }
    242
    243    public String getLogFile() {
    244        return logFile;
    245    }
    246
    247    public void setLogFile(final String logFile) {
    248        this.logFile = logFile;
    249    }
    250
    251    private void addOrUpdateAttributeActions(final List prototypes, final Map attributeActions, final int tag) {
    252        if ((attributeActions != null) && (attributeActions.size() > 0)) {
    253            String name, action;
    254            boolean prototypeExists;
    255            NewAttribute newAttribute;
    256            for (final Iterator iteratorI = attributeActions.keySet().iterator(); iteratorI.hasNext();) {
    257                name = (String) iteratorI.next();
    258                action = (String) attributeActions.get(name);
    259                prototypeExists = false;
    260                for (final Iterator iteratorJ = prototypes.iterator(); iteratorJ.hasNext();) {
    261                    newAttribute = (NewAttribute) iteratorJ.next();
    262                    if (newAttribute.type.equals(name)) {
    263                        // if the attribute exists, update its context
    264                        newAttribute.addContext(tag);
    265                        prototypeExists = true;
    266                        break;
    267                    }
    268                }
    269                // if no attribute is found, add a new attribute
    270                if (!prototypeExists) {
    271                    if (ERROR.equals(action)) {
    272                        newAttribute = new NewAttribute.ErrorAttribute(name, tag);
    273                    } else if (STRIP.equals(action)) {
    274                        newAttribute = new NewAttribute.StripAttribute(name, tag);
    275                    } else if (PASS.equals(action)) {
    276                        newAttribute = new NewAttribute.PassAttribute(name, tag);
    277                    } else {
    278                        newAttribute = new NewAttribute(name, action, tag);
    279                    }
    280                    prototypes.add(newAttribute);
    281                }
    282            }
    283        }
    284    }
    285
    286    public Attribute[] getUnknownAttributePrototypes() {
    287        if (unknownAttributeTypes == null) {
    288            final List prototypes = new ArrayList();
    289            addOrUpdateAttributeActions(prototypes, classAttributeActions, AttributeDefinitionBands.CONTEXT_CLASS);
    290
    291            addOrUpdateAttributeActions(prototypes, methodAttributeActions, AttributeDefinitionBands.CONTEXT_METHOD);
    292
    293            addOrUpdateAttributeActions(prototypes, fieldAttributeActions, AttributeDefinitionBands.CONTEXT_FIELD);
    294
    295            addOrUpdateAttributeActions(prototypes, codeAttributeActions, AttributeDefinitionBands.CONTEXT_CODE);
    296
    297            unknownAttributeTypes = (Attribute[]) prototypes.toArray(new Attribute[0]);
    298        }
    299        return unknownAttributeTypes;
    300    }
    301
    302    public String getUnknownClassAttributeAction(final String type) {
    303        if (classAttributeActions == null) {
    304            return unknownAttributeAction;
    305        }
    306        String action = (String) classAttributeActions.get(type);
    307        if (action == null) {
    308            action = unknownAttributeAction;
    309        }
    310        return action;
    311    }
    312
    313    public String getUnknownMethodAttributeAction(final String type) {
    314        if (methodAttributeActions == null) {
    315            return unknownAttributeAction;
    316        }
    317        String action = (String) methodAttributeActions.get(type);
    318        if (action == null) {
    319            action = unknownAttributeAction;
    320        }
    321        return action;
    322    }
    323
    324    public String getUnknownFieldAttributeAction(final String type) {
    325        if (fieldAttributeActions == null) {
    326            return unknownAttributeAction;
    327        }
    328        String action = (String) fieldAttributeActions.get(type);
    329        if (action == null) {
    330            action = unknownAttributeAction;
    331        }
    332        return action;
    333    }
    334
    335    public String getUnknownCodeAttributeAction(final String type) {
    336        if (codeAttributeActions == null) {
    337            return unknownAttributeAction;
    338        }
    339        String action = (String) codeAttributeActions.get(type);
    340        if (action == null) {
    341            action = unknownAttributeAction;
    342        }
    343        return action;
    344    }
    345
    346}