Statistics
| Revision:

root / import / ext3D / trunk / install-extension3d / IzPack / src / lib / com / izforge / izpack / installer / VariableSubstitutor.java @ 15280

History | View | Annotate | Download (13 KB)

1
/*
2
 *  $Id: VariableSubstitutor.java,v 1.1 2006/06/14 07:29:07 cesar Exp $
3
 *  IzPack
4
 *  Copyright (C) 2001 Johannes Lehtinen
5
 *
6
 *  File :               VariableSubstitutor.java
7
 *  Description :        Variable substitutor backend.
8
 *  Author's email :     johannes.lehtinen@iki.fi
9
 *  Author's Website :   http://www.iki.fi/jle/
10
 *
11
 *  This program is free software; you can redistribute it and/or
12
 *  modify it under the terms of the GNU General Public License
13
 *  as published by the Free Software Foundation; either version 2
14
 *  of the License, or any later version.
15
 *
16
 *  This program is distributed in the hope that it will be useful,
17
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 *  GNU General Public License for more details.
20
 *
21
 *  You should have received a copy of the GNU General Public License
22
 *  along with this program; if not, write to the Free Software
23
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24
 */
25
package com.izforge.izpack.installer;
26

    
27
import java.io.IOException;
28
import java.io.InputStream;
29
import java.io.InputStreamReader;
30
import java.io.OutputStream;
31
import java.io.OutputStreamWriter;
32
import java.io.Reader;
33
import java.io.Serializable;
34
import java.io.StringReader;
35
import java.io.StringWriter;
36
import java.io.UnsupportedEncodingException;
37
import java.io.Writer;
38
import java.util.HashMap;
39
import java.util.Map;
40

    
41
import com.izforge.izpack.util.IoHelper;
42

    
43
/**
44
 *  Substitutes variables occurring in an input stream or a string. This
45
 *  implementation supports a generic variable value mapping and escapes the
46
 *  possible special characters occurring in the substituted values. The file
47
 *  types specifically supported are plain text files (no escaping), Java
48
 *  properties files, and XML files. A valid variable name matches the regular
49
 *  expression [a-zA-Z][a-zA-Z0-9_]* and names are case sensitive. Variables are
50
 *  referenced either by $NAME or ${NAME} (the latter syntax being useful in
51
 *  situations like ${NAME}NOTPARTOFNAME). If a referenced variable is undefined
52
 *  then it is not substituted but the corresponding part of the stream is
53
 *  copied as is.
54
 *
55
 * @author     Johannes Lehtinen <johannes.lehtinen@iki.fi>
56
 */
57
public class VariableSubstitutor implements Serializable
58
{
59

    
60
  /**  The variable value mappings */
61
  protected transient Map variables;
62

    
63
  /**  Whether braces are required for substitution. */
64
  protected boolean bracesRequired = false;
65

    
66
  /**  A constant for file type. Plain file. */
67
  protected final static int TYPE_PLAIN = 0;
68

    
69
  /**  A constant for file type. Java properties file. */
70
  protected final static int TYPE_JAVA_PROPERTIES = 1;
71

    
72
  /**  A constant for file type. XML file. */
73
  protected final static int TYPE_XML = 2;
74

    
75
  /**  A constant for file type. Shell file. */
76
  protected final static int TYPE_SHELL = 3;
77

    
78
  /**  A constant for file type. Plain file with '@' start char. */
79
  protected final static int TYPE_AT = 4;
80

    
81
  /**  A mapping of file type names to corresponding integer constants. */
82
  protected final static Map typeNameToConstantMap;
83

    
84
  // Initialize the file type map
85
  static
86
  {
87
    typeNameToConstantMap = new HashMap();
88
    typeNameToConstantMap.put("plain", new Integer(TYPE_PLAIN));
89
    typeNameToConstantMap.put("javaprop",
90
      new Integer(TYPE_JAVA_PROPERTIES));
91
    typeNameToConstantMap.put("xml", new Integer(TYPE_XML));
92
    typeNameToConstantMap.put("shell", new Integer(TYPE_SHELL));
93
    typeNameToConstantMap.put("at", new Integer(TYPE_AT));
94
  }
95

    
96

    
97
  /**
98
   *  Constructs a new substitutor using the specified variable value mappings.
99
   *  The environment hashtable is copied by reference. Braces are not required
100
   *  by default
101
   *
102
   * @param  variables  the map with variable value mappings
103
   */
104
  public VariableSubstitutor(Map variables)
105
  {
106
    this.variables = variables;
107
  }
108

    
109
  /**
110
   * Get whether this substitutor requires braces.
111
   */
112
  public boolean areBracesRequired() {
113
    return bracesRequired;
114
  }
115

    
116
  /**
117
   * Specify whether this substitutor requires braces.
118
   */
119
  public void setBracesRequired(boolean braces) {
120
    bracesRequired = braces;
121
  }
122

    
123

    
124
  /**
125
   *  Substitutes the variables found in the specified string. Escapes special
126
   *  characters using file type specific escaping if necessary.
127
   *
128
   * @param  str                           the string to check for variables
129
   * @param  type                          the escaping type or null for plain
130
   * @return                               the string with substituted variables
131
   * @exception  IllegalArgumentException  if unknown escaping type specified
132
   */
133
  public String substitute(String str, String type)
134
     throws IllegalArgumentException
135
  {
136
    if (str == null) return null;
137
    
138
    // Create reader and writer for the strings
139
    StringReader reader = new StringReader(str);
140
    StringWriter writer = new StringWriter();
141

    
142
    // Substitute any variables
143
    try
144
    {
145
      substitute(reader, writer, type);
146
    }
147
    catch (IOException e)
148
    {
149
      throw new Error
150
        ("Unexpected I/O exception when reading/writing memory "
151
         + "buffer; nested exception is: " + e);
152
    }
153

    
154
    // Return the resulting string
155
    return writer.getBuffer().toString();
156
  }
157

    
158

    
159
  /**
160
   *  Substitutes the variables found in the specified input stream. Escapes
161
   *  special characters using file type specific escaping if necessary.
162
   *
163
   * @param  in                                the input stream to read
164
   * @param  out                               the output stream to write
165
   * @param  type                              the file type or null for plain
166
   * @param  encoding                          the character encoding or null
167
   *      for default
168
   * @exception  IllegalArgumentException      if unknown file type specified
169
   * @exception  UnsupportedEncodingException  if encoding not supported
170
   * @exception  IOException                   if an I/O error occurs
171
   */
172
  public void substitute(InputStream in, OutputStream out,
173
                         String type, String encoding)
174
     throws IllegalArgumentException, UnsupportedEncodingException,
175
    IOException
176
  {
177
    // Check if file type specific default encoding known
178
    if (encoding == null)
179
    {
180
      int t = getTypeConstant(type);
181
      switch (t)
182
      {
183
          case TYPE_JAVA_PROPERTIES:
184
            encoding = "ISO-8859-1";
185
            break;
186
          case TYPE_XML:
187
            encoding = "UTF-8";
188
            break;
189
      }
190
    }
191

    
192
    // Create the reader and writer
193
    InputStreamReader reader =
194
      (encoding != null ?
195
      new InputStreamReader(in, encoding) : new InputStreamReader(in));
196
    OutputStreamWriter writer =
197
      (encoding != null ?
198
      new OutputStreamWriter(out, encoding) :
199
      new OutputStreamWriter(out));
200

    
201
    // Copy the data and substitute variables
202
    substitute(reader, writer, type);
203

    
204
    // Flush the writer so that everything gets written out
205
    writer.flush();
206
  }
207

    
208

    
209
  /**
210
   *  Substitutes the variables found in the data read from the specified
211
   *  reader. Escapes special characters using file type specific escaping if
212
   *  necessary.
213
   *
214
   * @param  reader                        the reader to read
215
   * @param  writer                        the writer used to write data out
216
   * @param  type                          the file type or null for plain
217
   * @exception  IllegalArgumentException  if unknown file type specified
218
   * @exception  IOException               if an I/O error occurs
219
   */
220
  public void substitute(Reader reader, Writer writer, String type)
221
     throws IllegalArgumentException, IOException
222
  {
223
    // Check the file type
224
    int t = getTypeConstant(type);
225

    
226
    // determine character which starts a variable
227
    char variable_start = '$';
228
    if (t == TYPE_SHELL)
229
      variable_start = '%';
230
    else if (t == TYPE_AT)
231
      variable_start = '@';
232

    
233
    // Copy data and substitute variables
234
    int c = reader.read();
235
    while (true)
236
    {
237
      // Find the next potential variable reference or EOF
238
      while (c != -1 && c != variable_start)
239
      {
240
        writer.write(c);
241
        c = reader.read();
242
      }
243
      if (c == -1)
244
        return;
245

    
246
      // Check if braces used or start char escaped
247
      boolean braces = false;
248
      c = reader.read();
249
      if (c == '{')
250
      {
251
        braces = true;
252
        c = reader.read();
253
      }
254
      else if (bracesRequired)
255
      {
256
        writer.write(variable_start);
257
        continue;
258
      }
259
      else if (c == -1)
260
      {
261
        writer.write(variable_start);
262
        return;
263
      }
264

    
265
      // Read the variable name
266
      StringBuffer nameBuffer = new StringBuffer();
267
      while (c != -1 &&
268
             (braces && c != '}') ||
269
             (c >= 'a' && c <= 'z') ||
270
             (c >= 'A' && c <= 'Z') ||
271
             (braces && (c == '[') || (c == ']')) ||
272
             (((c >= '0' && c <= '9') || c == '_') && nameBuffer.length() > 0))
273
      {
274
        nameBuffer.append((char) c);
275
        c = reader.read();
276
      }
277
      String name = nameBuffer.toString();
278

    
279
      // Check if a legal and defined variable found
280
      String varvalue = null;
281
      
282
      if ((!braces || c == '}') && name.length() > 0)
283
      {
284
        // check for environment variables
285
        if (braces && name.startsWith("ENV[") && (name.lastIndexOf(']') == name.length()-1))
286
        {
287
          varvalue = IoHelper.getenv(name.substring(4, name.length()-1));
288
        }
289
        else
290
          varvalue = (String)variables.get(name);
291
      }
292

    
293
      // Substitute the variable...
294
      if (varvalue != null)
295
      {
296
        writer.write
297
          (escapeSpecialChars(varvalue, t));
298
        if (braces)
299
          c = reader.read();
300
      }
301
      // ...or ignore it
302
      else
303
      {
304
        writer.write(variable_start);
305
        if (braces)
306
          writer.write('{');
307
        writer.write(name);
308
      }
309
    }
310
  }
311

    
312

    
313
  /**
314
   *  Returns the internal constant for the specified file type.
315
   *
316
   * @param  type                          the type name or null for plain
317
   * @return                               the file type constant
318
   */
319
  protected int getTypeConstant(String type)
320
  {
321
    if (type == null)
322
      return TYPE_PLAIN;
323
    Integer integer = (Integer) typeNameToConstantMap.get(type);
324
    if (integer == null)
325
      throw new IllegalArgumentException
326
        ("Unknown file type " + type);
327
    else
328
      return integer.intValue();
329
  }
330

    
331

    
332
  /**
333
   *  Escapes the special characters in the specified string using file type
334
   *  specific rules.
335
   *
336
   * @param  str   the string to check for special characters
337
   * @param  type  the target file type (one of TYPE_xxx)
338
   * @return       the string with the special characters properly escaped
339
   */
340
  protected String escapeSpecialChars(String str, int type)
341
  {
342
    StringBuffer buffer;
343
    int len;
344
    int i;
345
    switch (type)
346
    {
347
        case TYPE_PLAIN:
348
        case TYPE_SHELL:
349
        case TYPE_AT:
350
          return str;
351
        case TYPE_JAVA_PROPERTIES:
352
          buffer = new StringBuffer(str);
353
          len = str.length();
354
          for (i = 0; i < len; i++)
355
          {
356
            // Check for control characters
357
            char c = buffer.charAt(i);
358
            if (c == '\t' || c == '\n' || c == '\r')
359
            {
360
              char tag;
361
              if (c == '\t')
362
                tag = 't';
363
              else if (c == '\n')
364
                tag = 'n';
365
              else
366
                tag = 'r';
367
              buffer.replace(i, i + 1, "\\" + tag);
368
              len++;
369
              i++;
370
            }
371

    
372
            // Check for special characters
373
            if (c == '\\' || c == '"' || c == '\'' || c == ' ')
374
            {
375
              buffer.insert(i, '\\');
376
              len++;
377
              i++;
378
            }
379
          }
380
          return buffer.toString();
381
        case TYPE_XML:
382
          buffer = new StringBuffer(str);
383
          len = str.length();
384
          for (i = 0; i < len; i++)
385
          {
386
            String r = null;
387
            char c = buffer.charAt(i);
388
            switch (c)
389
            {
390
                case '<':
391
                  r = "&lt;";
392
                  break;
393
                case '>':
394
                  r = "&gt;";
395
                  break;
396
                case '&':
397
                  r = "&amp;";
398
                  break;
399
                case '\'':
400
                  r = "&apos;";
401
                  break;
402
                case '"':
403
                  r = "&quot;";
404
                  break;
405
            }
406
            if (r != null)
407
            {
408
              buffer.replace(i, i + 1, r);
409
              len = buffer.length();
410
              i += r.length() - 1;
411
            }
412
          }
413
          return buffer.toString();
414
        default:
415
          throw new Error
416
            ("Unknown file type constant " + type);
417
    }
418
  }
419
}
420