1 /*
   2  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.java.util.jar.pack;
  27 
  28 import java.beans.PropertyChangeListener;
  29 import java.beans.PropertyChangeEvent;
  30 import java.io.BufferedInputStream;
  31 import java.io.IOException;
  32 import java.io.InputStream;
  33 import java.io.PrintStream;
  34 import java.io.PrintWriter;
  35 import java.util.ArrayList;
  36 import java.util.Collection;
  37 import java.util.Comparator;
  38 import java.util.HashMap;
  39 import java.util.List;
  40 import java.util.Map;
  41 import java.util.Properties;
  42 import java.util.Set;
  43 import java.util.SortedMap;
  44 import java.util.TreeMap;
  45 import java.util.jar.Pack200;
  46 /**
  47  * Control block for publishing Pack200 options to the other classes.
  48  */
  49 
  50 final class PropMap implements SortedMap<Object, Object>  {
  51     private final TreeMap<Object, Object> theMap = new TreeMap<>();;
  52     private final List<PropertyChangeListener> listenerList = new ArrayList<>(1);
  53 
  54     void addListener(PropertyChangeListener listener) {
  55         listenerList.add(listener);
  56     }
  57 
  58     void removeListener(PropertyChangeListener listener) {
  59         listenerList.remove(listener);
  60     }
  61 
  62     void addListeners(ArrayList<PropertyChangeListener> listeners) {
  63         listenerList.addAll(listeners);
  64     }
  65 
  66     void removeListeners(ArrayList<PropertyChangeListener> listeners) {
  67         listenerList.removeAll(listeners);
  68     }
  69 
  70     // Override:
  71     public Object put(Object key, Object value) {
  72         Object oldValue = theMap.put(key, value);
  73         if (value != oldValue && !listenerList.isEmpty()) {
  74             // Post the property change event.
  75             PropertyChangeEvent event =
  76                 new PropertyChangeEvent(this, (String) key,
  77                                         oldValue, value);
  78             for (PropertyChangeListener listener : listenerList) {
  79                 listener.propertyChange(event);
  80             }
  81         }
  82         return oldValue;
  83     }
  84 
  85     // All this other stuff is private to the current package.
  86     // Outide clients of Pack200 do not need to use it; they can
  87     // get by with generic SortedMap functionality.
  88     private static Map<Object, Object> defaultProps;
  89     static {
  90         Properties props = new Properties();
  91 
  92         // Allow implementation selected via -Dpack.disable.native=true
  93         props.put(Utils.DEBUG_DISABLE_NATIVE,
  94                   String.valueOf(Boolean.getBoolean(Utils.DEBUG_DISABLE_NATIVE)));
  95 
  96         // Set the DEBUG_VERBOSE from system
  97         props.put(Utils.DEBUG_VERBOSE,
  98                   String.valueOf(Integer.getInteger(Utils.DEBUG_VERBOSE,0)));
  99 
 100         // Set the PACK_TIMEZONE_NO_UTC
 101         props.put(Utils.PACK_DEFAULT_TIMEZONE,
 102                   String.valueOf(Boolean.getBoolean(Utils.PACK_DEFAULT_TIMEZONE)));
 103 
 104         // The segment size is unlimited
 105         props.put(Pack200.Packer.SEGMENT_LIMIT, "-1");
 106 
 107         // Preserve file ordering by default.
 108         props.put(Pack200.Packer.KEEP_FILE_ORDER, Pack200.Packer.TRUE);
 109 
 110         // Preserve all modification times by default.
 111         props.put(Pack200.Packer.MODIFICATION_TIME, Pack200.Packer.KEEP);
 112 
 113         // Preserve deflation hints by default.
 114         props.put(Pack200.Packer.DEFLATE_HINT, Pack200.Packer.KEEP);
 115 
 116         // Pass through files with unrecognized attributes by default.
 117         props.put(Pack200.Packer.UNKNOWN_ATTRIBUTE, Pack200.Packer.PASS);
 118 
 119         // Default effort is 5, midway between 1 and 9.
 120         props.put(Pack200.Packer.EFFORT, "5");
 121 
 122         // Define certain attribute layouts by default.
 123         // Do this after the previous props are put in place,
 124         // to allow override if necessary.
 125         InputStream propStr = null;
 126         try {
 127             String propFile = "intrinsic.properties";
 128             propStr = PackerImpl.class.getResourceAsStream(propFile);
 129             props.load(new BufferedInputStream(propStr));
 130             for (Map.Entry<Object, Object> e : props.entrySet()) {
 131                 String key = (String) e.getKey();
 132                 String val = (String) e.getValue();
 133                 if (key.startsWith("attribute.")) {
 134                     e.setValue(Attribute.normalizeLayoutString(val));
 135                 }
 136             }
 137         } catch (IOException ee) {
 138             throw new RuntimeException(ee);
 139         } finally {
 140             try {
 141                 if (propStr != null) {
 142                     propStr.close();
 143                 }
 144             } catch (IOException ignore) {}
 145         }
 146 
 147         defaultProps = (new HashMap<>(props));  // shrink to fit
 148     }
 149 
 150     PropMap() {
 151         theMap.putAll(defaultProps);
 152     }
 153 
 154     // Return a view of this map which includes only properties
 155     // that begin with the given prefix.  This is easy because
 156     // the map is sorted, and has a subMap accessor.
 157     SortedMap<Object, Object> prefixMap(String prefix) {
 158         int len = prefix.length();
 159         if (len == 0)
 160             return this;
 161         char nextch = (char)(prefix.charAt(len-1) + 1);
 162         String limit = prefix.substring(0, len-1)+nextch;
 163         //System.out.println(prefix+" => "+subMap(prefix, limit));
 164         return subMap(prefix, limit);
 165     }
 166 
 167     String getProperty(String s) {
 168         return (String) get(s);
 169     }
 170     String getProperty(String s, String defaultVal) {
 171         String val = getProperty(s);
 172         if (val == null)
 173             return defaultVal;
 174         return val;
 175     }
 176     String setProperty(String s, String val) {
 177         return (String) put(s, val);
 178     }
 179 
 180     // Get sequence of props for "prefix", and "prefix.*".
 181     List getProperties(String prefix) {
 182         Collection<Object> values = prefixMap(prefix).values();
 183         List<Object> res = new ArrayList<>(values.size());
 184         res.addAll(values);
 185         while (res.remove(null));
 186         return res;
 187     }
 188 
 189     private boolean toBoolean(String val) {
 190         return Boolean.valueOf(val).booleanValue();
 191     }
 192     boolean getBoolean(String s) {
 193         return toBoolean(getProperty(s));
 194     }
 195     boolean setBoolean(String s, boolean val) {
 196         return toBoolean(setProperty(s, String.valueOf(val)));
 197     }
 198 
 199     int toInteger(String val) {
 200         if (val == null)  return 0;
 201         if (Pack200.Packer.TRUE.equals(val))   return 1;
 202         if (Pack200.Packer.FALSE.equals(val))  return 0;
 203         return Integer.parseInt(val);
 204     }
 205     int getInteger(String s) {
 206         return toInteger(getProperty(s));
 207     }
 208     int setInteger(String s, int val) {
 209         return toInteger(setProperty(s, String.valueOf(val)));
 210     }
 211 
 212     long toLong(String val) {
 213         try {
 214             return val == null ? 0 : Long.parseLong(val);
 215         } catch (java.lang.NumberFormatException nfe) {
 216             throw new IllegalArgumentException("Invalid value");
 217         }
 218     }
 219     long getLong(String s) {
 220         return toLong(getProperty(s));
 221     }
 222     long setLong(String s, long val) {
 223         return toLong(setProperty(s, String.valueOf(val)));
 224     }
 225 
 226     int getTime(String s) {
 227         String sval = getProperty(s, "0");
 228         if (Utils.NOW.equals(sval)) {
 229             return (int)((System.currentTimeMillis()+500)/1000);
 230         }
 231         long lval = toLong(sval);
 232         final long recentSecondCount = 1000000000;
 233 
 234         if (lval < recentSecondCount*10 && !"0".equals(sval))
 235             Utils.log.warning("Supplied modtime appears to be seconds rather than milliseconds: "+sval);
 236 
 237         return (int)((lval+500)/1000);
 238     }
 239 
 240     void list(PrintStream out) {
 241         PrintWriter outw = new PrintWriter(out);
 242         list(outw);
 243         outw.flush();
 244     }
 245     void list(PrintWriter out) {
 246         out.println("#"+Utils.PACK_ZIP_ARCHIVE_MARKER_COMMENT+"[");
 247         Set defaults = defaultProps.entrySet();
 248         for (Map.Entry e : theMap.entrySet()) {
 249             if (defaults.contains(e))  continue;
 250             out.println("  " + e.getKey() + " = " + e.getValue());
 251         }
 252         out.println("#]");
 253     }
 254 
 255     @Override
 256     public int size() {
 257         return theMap.size();
 258     }
 259 
 260     @Override
 261     public boolean isEmpty() {
 262         return theMap.isEmpty();
 263     }
 264 
 265     @Override
 266     public boolean containsKey(Object key) {
 267         return theMap.containsKey(key);
 268     }
 269 
 270     @Override
 271     public boolean containsValue(Object value) {
 272         return theMap.containsValue(value);
 273     }
 274 
 275     @Override
 276     public Object get(Object key) {
 277         return theMap.get(key);
 278     }
 279 
 280     @Override
 281     public Object remove(Object key) {
 282        return theMap.remove(key);
 283     }
 284 
 285     @Override
 286     @SuppressWarnings("unchecked")
 287     public void putAll(Map m) {
 288        theMap.putAll(m);
 289     }
 290 
 291     @Override
 292     public void clear() {
 293         theMap.clear();
 294     }
 295 
 296     @Override
 297     public Set<Object> keySet() {
 298        return theMap.keySet();
 299     }
 300 
 301     @Override
 302     public Collection<Object> values() {
 303        return theMap.values();
 304     }
 305 
 306     @Override
 307     public Set<Map.Entry<Object, Object>> entrySet() {
 308         return theMap.entrySet();
 309     }
 310 
 311     @Override
 312     @SuppressWarnings("unchecked")
 313     public Comparator<Object> comparator() {
 314         return (Comparator<Object>) theMap.comparator();
 315     }
 316 
 317     @Override
 318     public SortedMap<Object, Object> subMap(Object fromKey, Object toKey) {
 319         return theMap.subMap(fromKey, toKey);
 320     }
 321 
 322     @Override
 323     public SortedMap<Object, Object> headMap(Object toKey) {
 324         return theMap.headMap(toKey);
 325     }
 326 
 327     @Override
 328     public SortedMap<Object, Object> tailMap(Object fromKey) {
 329         return theMap.tailMap(fromKey);
 330     }
 331 
 332     @Override
 333     public Object firstKey() {
 334         return theMap.firstKey();
 335     }
 336 
 337     @Override
 338     public Object lastKey() {
 339        return theMap.lastKey();
 340     }
 341 }