1 /* 2 * $Id$ 3 * 4 * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. 5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6 * 7 * This code is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 only, as 9 * published by the Free Software Foundation. Oracle designates this 10 * particular file as subject to the "Classpath" exception as provided 11 * by Oracle in the LICENSE file that accompanied this code. 12 * 13 * This code is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 * version 2 for more details (a copy is included in the LICENSE file that 17 * accompanied this code). 18 * 19 * You should have received a copy of the GNU General Public License version 20 * 2 along with this work; if not, write to the Free Software Foundation, 21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 22 * 23 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 24 * or visit www.oracle.com if you need additional information or have any 25 * questions. 26 */ 27 package com.sun.javatest.agent; 28 29 import java.io.Reader; 30 import java.io.BufferedReader; 31 import java.io.InputStreamReader; 32 import java.io.IOException; 33 import java.lang.reflect.Constructor; 34 import java.lang.reflect.InvocationTargetException; 35 import java.net.URL; 36 import java.nio.charset.StandardCharsets; 37 import java.util.Enumeration; 38 import java.util.Vector; 39 import com.sun.javatest.util.StringArray; 40 import java.io.PrintStream; 41 42 /** 43 * A map provides a simple translation table for between configuration values 44 * as provided by JT Harness and as required by an Agent. Typically, this is for 45 * adjusting filenames because of differences introduced by different mount points 46 * for various file systems. 47 * <p> 48 * This facility has been largely superceded by the map substitution mechanism 49 * provided by environment files. 50 */ 51 public class Map { 52 /** 53 * Read a map from a specified file. This code is deliberately 54 * written to tolerate Java platforms without a file system ... 55 * as in some versions of Personal Java. Any problems caused by 56 * the file system not being present are returned as IOExceptions 57 * with a suitable detail message. 58 * @param name The name of the file to read 59 * @return The map read from the given file 60 * @throws IOException if any errors occurred reading the file 61 */ 62 public static Map readFile(String name) throws IOException { 63 try { 64 Class<?> c = Class.forName("java.io.FileReader"); // optional API in Jersonal Java 65 Constructor m = c.getConstructor(new Class[] {String.class}); 66 Reader r = (Reader)(m.newInstance(new Object[] {name})); 67 return new Map(r); 68 } 69 catch (ClassNotFoundException e) { 70 throw new IOException("file system not accessible (" + e + ")"); 71 } 72 catch (InvocationTargetException e) { 73 Throwable t = e.getTargetException(); 74 if (t instanceof IOException) 75 throw (IOException)t; 76 else 77 throw fileSystemProblem(t); 78 } 79 catch (IllegalAccessException e) { 80 throw fileSystemProblem(e); 81 } 82 catch (InstantiationException e) { 83 throw fileSystemProblem(e); 84 } 85 catch (NoSuchMethodException e) { 86 throw fileSystemProblem(e); 87 } 88 catch (Error e) { 89 throw fileSystemProblem(e); 90 } 91 } 92 93 /** 94 * Read a map from a URL. 95 * @param u The URL to read 96 * @return The map read from the given URL 97 * @throws IOException if any errors occurred reading the URL 98 */ 99 public static Map readURL(URL u) throws IOException { 100 return new Map(new InputStreamReader(u.openStream(), StandardCharsets.UTF_8.name())); 101 } 102 103 104 /** 105 * Read a map from a local file (if the name does not begin with http:) 106 * or from a URL if it does. The method is simply a wrapper that delegates 107 * to either {@link #readFile} or {@link #readURL} depending on the 108 * argument. 109 * @param name The name of the file or URL to read 110 * @return The map read from the given location 111 * @throws IOException if any errors occurred reading the map 112 */ 113 public static Map readFileOrURL(String name) throws IOException { 114 if (name.length() > 5 && name.substring(0, 5).equalsIgnoreCase("http:")) 115 return readURL(new URL(name)); 116 else 117 return readFile(name); 118 } 119 120 private static IOException fileSystemProblem(Throwable t) { 121 return new IOException("problem accessing file system: " + t); 122 } 123 124 /** 125 * Create a map by reading it from a given stream. 126 * The '\u0020' sequence could be used to specify space symbol in the values. 127 * @param r The reader from which to read the map data. The reader is closed 128 * after it has been completely read 129 * @throws IOException if problems occur while reading the map data. 130 */ 131 public Map(Reader r) throws IOException { 132 BufferedReader in = 133 (r instanceof BufferedReader ? (BufferedReader)r : new BufferedReader(r)) 134 ; 135 // data arrives in rows, but we want it in columns 136 Vector<String> from = new Vector<>(); 137 Vector<String> to = new Vector<>(); 138 String line; 139 while ((line = in.readLine()) != null) { 140 line = line.trim(); 141 if (line.length() > 0 && !line.startsWith("#")) { 142 String[] row = StringArray.split(line); 143 if (row.length < 2) 144 throw new IOException("format error in map file, line is: " + line); 145 from.addElement(row[0].replaceAll("\\Q\\u0020\\E", " ")); 146 to.addElement(row[1].replaceAll("\\Q\\u0020\\E", " ")); 147 } 148 } 149 in.close(); 150 151 fromValues = new String[from.size()]; 152 from.copyInto(fromValues); 153 toValues = new String[to.size()]; 154 to.copyInto(toValues); 155 } 156 157 /** 158 * Translate the strings according to values in the map. 159 * The strings are updated in place. 160 * @param args An array of strings to be translated according to the data 161 * the map. 162 */ 163 public void map(String[] args) { 164 if (fromValues == null) 165 return; // empty table 166 167 for (int i = 0; i < args.length; i++) { 168 String arg = args[i]; 169 for (int j = 0; j < fromValues.length; j++) { 170 String f = fromValues[j]; 171 String t = toValues[j]; 172 for (int index = arg.indexOf(f); 173 index != -1; 174 index = arg.indexOf(f, index + t.length())) { 175 arg = arg.substring(0, index) + t + arg.substring(index + f.length()); 176 } 177 } 178 if (tracing && arg != args[i]) { 179 traceOut.println("MAPARG-from: " + args[i]); 180 traceOut.println("MAPARG-to: " + arg); 181 } 182 args[i] = arg; 183 } 184 } 185 186 /** 187 * Creates a map from the specified <code>java.util.Map</code>. 188 * 189 * @param map the java.util.Map instance to take key-value pairs from 190 */ 191 public Map(java.util.Map<String, String> map) { 192 fromValues = new String[map.size()]; 193 toValues = new String[map.size()]; 194 int index = 0; 195 for (java.util.Map.Entry<String, String> entry : map.entrySet()) { 196 fromValues[index] = entry.getKey(); 197 toValues[index] = entry.getValue(); 198 index++; 199 } 200 } 201 202 /** 203 * Enumerate the entries of the map. 204 * @return an enumeration of the translation entries within the map 205 */ 206 public Enumeration<String[]> enumerate() { 207 Vector<String[]> v = new Vector<>(fromValues.length); 208 for (int i = 0; i < fromValues.length; i++) { 209 v.addElement(new String[] {fromValues[i], toValues[i]}); 210 } 211 return v.elements(); 212 } 213 214 public void setTracing(boolean state, PrintStream out) { 215 tracing = state; 216 if (state) { 217 traceOut = out; 218 } 219 else { 220 traceOut = null; 221 } 222 } 223 224 private boolean tracing; 225 private PrintStream traceOut; 226 private String[] fromValues; 227 private String[] toValues; 228 }