0001: /*
0002: * xtc - The eXTensible Compiler
0003: * Copyright (C) 2005-2007 Robert Grimm
0004: *
0005: * This library is free software; you can redistribute it and/or
0006: * modify it under the terms of the GNU Lesser General Public License
0007: * version 2.1 as published by the Free Software Foundation.
0008: *
0009: * This library is distributed in the hope that it will be useful,
0010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0012: * Lesser General Public License for more details.
0013: *
0014: * You should have received a copy of the GNU Lesser General Public
0015: * License along with this library; if not, write to the Free Software
0016: * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
0017: * USA.
0018: */
0019: package xtc.util;
0020:
0021: import java.io.BufferedReader;
0022: import java.io.BufferedWriter;
0023: import java.io.File;
0024: import java.io.FileInputStream;
0025: import java.io.FileNotFoundException;
0026: import java.io.FileOutputStream;
0027: import java.io.InputStream;
0028: import java.io.InputStreamReader;
0029: import java.io.IOException;
0030: import java.io.OutputStream;
0031: import java.io.OutputStreamWriter;
0032: import java.io.Reader;
0033: import java.io.UnsupportedEncodingException;
0034: import java.io.Writer;
0035:
0036: import java.io.StringReader;
0037:
0038: import java.util.ArrayList;
0039: import java.util.HashMap;
0040: import java.util.List;
0041: import java.util.Map;
0042:
0043: import xtc.parser.ParseError;
0044: import xtc.parser.PParser;
0045: import xtc.parser.Result;
0046: import xtc.parser.SemanticValue;
0047:
0048: import xtc.tree.Attribute;
0049: import xtc.tree.Node;
0050: import xtc.tree.Printer;
0051:
0052: /**
0053: * A tool's runtime. This helper class processes command line
0054: * options, prints errors and warnings, and manages console output.
0055: *
0056: * @author Robert Grimm
0057: * @version $Revision: 1.27 $
0058: */
0059: public class Runtime {
0060:
0061: /**
0062: * The internal name for the input directory option. The option is
0063: * expected to have multiple directory values.
0064: */
0065: public static final String INPUT_DIRECTORY = "inputDirectory";
0066:
0067: /**
0068: * The internal name for the output directory option. The option is
0069: * expected to have a directory value.
0070: */
0071: public static final String OUTPUT_DIRECTORY = "outputDirectory";
0072:
0073: /**
0074: * The internal name for the intput encoding option. The option is
0075: * expected to have a word value.
0076: */
0077: public static final String INPUT_ENCODING = "inputEncoding";
0078:
0079: /**
0080: * The internal name for the output encoding option. The option is
0081: * expected to have a word value.
0082: */
0083: public static final String OUTPUT_ENCODING = "outputEncoding";
0084:
0085: // ========================================================================
0086:
0087: /** The console printer. */
0088: protected Printer console;
0089:
0090: /** The error console printer. */
0091: protected Printer errConsole;
0092:
0093: /** The list of command line options. */
0094: protected final List<Option> optionList;
0095:
0096: /** The map from external names to options. */
0097: protected final Map<String, Option> externalMap;
0098:
0099: /** The map from internal names to options. */
0100: protected final Map<String, Option> internalMap;
0101:
0102: /** The actual options. */
0103: protected final Map<String, Object> options;
0104:
0105: /** The error count. */
0106: protected int errors;
0107:
0108: /** The warning count. */
0109: protected int warnings;
0110:
0111: // ========================================================================
0112:
0113: /**
0114: * Create a new runtime. Note that the list of input directories is
0115: * empty, while the output directory is initialized to the current
0116: * directory.
0117: */
0118: public Runtime() {
0119: console = new Printer(new BufferedWriter(
0120: new OutputStreamWriter(System.out)));
0121: errConsole = new Printer(new BufferedWriter(
0122: new OutputStreamWriter(System.err)));
0123: optionList = new ArrayList<Option>();
0124: externalMap = new HashMap<String, Option>();
0125: internalMap = new HashMap<String, Option>();
0126: options = new HashMap<String, Object>();
0127: errors = 0;
0128: warnings = 0;
0129: }
0130:
0131: // ========================================================================
0132:
0133: /**
0134: * Get a printer to the console.
0135: *
0136: * @return A printer to the console.
0137: */
0138: public Printer console() {
0139: return console;
0140: }
0141:
0142: /**
0143: * Update the printer to the console. Since the console is used
0144: * throughout xtc, use this method with caution.
0145: *
0146: * @param console The new console.
0147: */
0148: public void setConsole(Printer console) {
0149: this .console = console;
0150: }
0151:
0152: /**
0153: * Get a printer to the error console.
0154: *
0155: * @return A printer to the error console.
0156: */
0157: public Printer errConsole() {
0158: return errConsole;
0159: }
0160:
0161: /**
0162: * Update the printer to the error console. Since the error console
0163: * is used throughout xtc, use this method with caution.
0164: *
0165: * @param console The new error console.
0166: */
0167: public void setErrConsole(Printer console) {
0168: errConsole = console;
0169: }
0170:
0171: // ========================================================================
0172:
0173: /**
0174: * Get an estimate of free memory.
0175: *
0176: * @return An estimate of free memory.
0177: */
0178: public long freeMemory() {
0179: return java.lang.Runtime.getRuntime().freeMemory();
0180: }
0181:
0182: // ========================================================================
0183:
0184: /**
0185: * Check that no option with the specified names exits.
0186: *
0187: * @param external The external name.
0188: * @param internal The internal name.
0189: * @throws IllegalArgumentException Signals that an option with
0190: * the external or interal name already exists.
0191: */
0192: protected void check(String external, String internal) {
0193: if (externalMap.containsKey(external)) {
0194: throw new IllegalArgumentException(
0195: "Option with external name " + external
0196: + " already exists");
0197: } else if (internalMap.containsKey(internal)) {
0198: throw new IllegalArgumentException(
0199: "Option with internal name " + internal
0200: + " already exists");
0201: }
0202: }
0203:
0204: /**
0205: * Add the specified option. This method adds the specified option
0206: * to the {@link #optionList}, {@link #externalMap}, and {@link
0207: * #internalMap} fields.
0208: *
0209: * @param option The option.
0210: */
0211: protected void add(Option option) {
0212: optionList.add(option);
0213: externalMap.put(option.external, option);
0214: internalMap.put(option.internal, option);
0215: }
0216:
0217: /**
0218: * Declare a boolean command line option.
0219: *
0220: * @param external The external name.
0221: * @param internal The internal name.
0222: * @param value The default value.
0223: * @param description The description.
0224: * @return This runtime.
0225: * @throws IllegalArgumentException Signals that an option with
0226: * the external or interal name already exists.
0227: */
0228: public Runtime bool(String external, String internal,
0229: boolean value, String description) {
0230: check(external, internal);
0231: add(new Option(Option.Kind.BOOLEAN, external, internal, value,
0232: false, description));
0233: return this ;
0234: }
0235:
0236: /**
0237: * Declare a word-valued command line option.
0238: *
0239: * @param external The external name.
0240: * @param internal The internal name.
0241: * @param multiple The flag for multiple occurrences.
0242: * @param description The description.
0243: * @throws IllegalArgumentException Signals that an option with
0244: * the external or interal name already exists.
0245: * @return This runtime.
0246: */
0247: public Runtime word(String external, String internal,
0248: boolean multiple, String description) {
0249: check(external, internal);
0250: add(new Option(Option.Kind.WORD, external, internal, null,
0251: multiple, description));
0252: return this ;
0253: }
0254:
0255: /**
0256: * Declare an integer-valued command line option.
0257: *
0258: * @param external The external name.
0259: * @param internal The internal name.
0260: * @param value The default value.
0261: * @param description The description.
0262: * @return This runtime.
0263: * @throws IllegalArgumentException Signals that an option with
0264: * the external or interal name already exists.
0265: */
0266: public Runtime number(String external, String internal, int value,
0267: String description) {
0268: check(external, internal);
0269: add(new Option(Option.Kind.INTEGER, external, internal,
0270: new Integer(value), false, description));
0271: return this ;
0272: }
0273:
0274: /**
0275: * Declare a file-valued command line option.
0276: *
0277: * @param external The external name.
0278: * @param internal The internal name.
0279: * @param multiple The flag for multiple occurrences.
0280: * @param description The description.
0281: * @return This runtime.
0282: * @throws IllegalArgumentException Signals that an option with
0283: * the external or interal name already exists.
0284: */
0285: public Runtime file(String external, String internal,
0286: boolean multiple, String description) {
0287: check(external, internal);
0288: add(new Option(Option.Kind.FILE, external, internal, null,
0289: multiple, description));
0290: return this ;
0291: }
0292:
0293: /**
0294: * Declare a directory-valued command line option. The default
0295: * value is the current directory.
0296: *
0297: * @param external The external name.
0298: * @param internal The internal name.
0299: * @param multiple The flag for multiple occurrences.
0300: * @param description The description.
0301: * @return This runtime.
0302: * @throws IllegalArgumentException Signals that an option with
0303: * the external or interal name already exists.
0304: */
0305: public Runtime dir(String external, String internal,
0306: boolean multiple, String description) {
0307: check(external, internal);
0308: add(new Option(Option.Kind.DIRECTORY, external, internal,
0309: new File(System.getProperty("user.dir")), multiple,
0310: description));
0311: return this ;
0312: }
0313:
0314: /**
0315: * Declare an attribute-valued command line option.
0316: *
0317: * @param external The external name.
0318: * @param internal The internal name.
0319: * @param multiple The flag for multiple occurrences.
0320: * @param description The description.
0321: * @throws IllegalArgumentException Signals that an option with
0322: * the external or interal name already exists.
0323: */
0324: public Runtime att(String external, String internal,
0325: boolean multiple, String description) {
0326: check(external, internal);
0327: add(new Option(Option.Kind.ATTRIBUTE, external, internal, null,
0328: multiple, description));
0329: return this ;
0330: }
0331:
0332: // ========================================================================
0333:
0334: /** Print a description of all command line options to the console. */
0335: public void printOptions() {
0336: // Determine the alignment across all options.
0337: int alignment = 0;
0338: for (Option option : optionList) {
0339: switch (option.kind) {
0340: case BOOLEAN:
0341: alignment = Math.max(alignment, option.external
0342: .length() + 5);
0343: break;
0344: case WORD:
0345: case FILE:
0346: alignment = Math.max(alignment, option.external
0347: .length() + 5 + 7);
0348: break;
0349: case INTEGER:
0350: case DIRECTORY:
0351: case ATTRIBUTE:
0352: alignment = Math.max(alignment, option.external
0353: .length() + 5 + 6);
0354: break;
0355: default:
0356: assert false : "Invalid option " + option;
0357: }
0358: }
0359:
0360: // Actually print all options.
0361: for (Option option : optionList) {
0362: console.p(" -").p(option.external);
0363: switch (option.kind) {
0364: case BOOLEAN:
0365: break;
0366: case WORD:
0367: console.p(" <word>");
0368: break;
0369: case INTEGER:
0370: console.p(" <num>");
0371: break;
0372: case FILE:
0373: console.p(" <file>");
0374: break;
0375: case DIRECTORY:
0376: console.p(" <dir>");
0377: break;
0378: case ATTRIBUTE:
0379: console.p(" <att>");
0380: break;
0381: default:
0382: assert false : "Invalid option " + option;
0383: }
0384:
0385: console.align(alignment)
0386: .wrap(alignment, option.description).pln();
0387: }
0388: console.flush();
0389: }
0390:
0391: // ========================================================================
0392:
0393: /**
0394: * Process the specified command line arguments. This method sets
0395: * all options to their specified values.
0396: *
0397: * @param args The arguments.
0398: * @return The index right after the processed command line options.
0399: */
0400: public int process(String args[]) {
0401: int index = 0;
0402: options.clear();
0403:
0404: while ((index < args.length) && args[index].startsWith("-")) {
0405: if (1 >= args[index].length()) {
0406: error("empty command line option");
0407:
0408: } else {
0409: String name = args[index].substring(1);
0410: Option option = externalMap.get(name);
0411:
0412: if (null == option) {
0413: error("unrecognized command line option " + name);
0414:
0415: } else if ((!option.multiple)
0416: && (options.containsKey(option.internal))) {
0417: error("repeated " + name + " option");
0418:
0419: } else if (Option.Kind.BOOLEAN == option.kind) {
0420: options.put(option.internal, Boolean.TRUE);
0421:
0422: } else if (args.length == index + 1) {
0423: error(name + " option without argument");
0424:
0425: } else {
0426: Object value = null;
0427: index++;
0428:
0429: switch (option.kind) {
0430: case WORD:
0431: value = args[index];
0432: break;
0433:
0434: case INTEGER:
0435: try {
0436: value = new Integer(args[index]);
0437: } catch (NumberFormatException x) {
0438: error("malformed integer argument to "
0439: + name + " option");
0440: }
0441: break;
0442:
0443: case FILE:
0444: File file = new File(args[index]);
0445: if (file.exists()) {
0446: value = file;
0447: } else {
0448: error("nonexistent file argument to "
0449: + name + " option");
0450: }
0451: break;
0452:
0453: case DIRECTORY:
0454: File dir = new File(args[index]);
0455: if (dir.exists()) {
0456: if (dir.isDirectory()) {
0457: value = dir;
0458: } else {
0459: error(args[index] + " not a directory");
0460: }
0461: } else {
0462: error("nonexistent directory argument to "
0463: + name + " option");
0464: }
0465: break;
0466:
0467: case ATTRIBUTE:
0468: PParser parser = new PParser(new StringReader(
0469: args[index]), "<console>", args[index]
0470: .length());
0471: Result result = null;
0472: try {
0473: result = parser.pAttribute(0);
0474: } catch (IOException x) {
0475: error("internal error: " + x);
0476: }
0477: if (!result.hasValue()) {
0478: error("malformed attribute " + args[index]
0479: + ": " + ((ParseError) result).msg);
0480:
0481: } else if (result.index != args[index].length()) {
0482: error("extra characters after "
0483: + args[index].substring(0,
0484: result.index));
0485:
0486: } else {
0487: value = ((SemanticValue) result).value;
0488: }
0489: break;
0490:
0491: default:
0492: assert false : "Unrecognized option " + option;
0493: }
0494:
0495: if (null != value) {
0496: if (option.multiple) {
0497: if (options.containsKey(option.internal)) {
0498: @SuppressWarnings("unchecked")
0499: List<Object> values = (List<Object>) options
0500: .get(option.internal);
0501: values.add(value);
0502:
0503: } else {
0504: List<Object> values = new ArrayList<Object>();
0505: values.add(value);
0506: options.put(option.internal, values);
0507: }
0508:
0509: } else {
0510: options.put(option.internal, value);
0511: }
0512: }
0513: }
0514: }
0515:
0516: index++;
0517: }
0518:
0519: return index;
0520: }
0521:
0522: // ========================================================================
0523:
0524: /**
0525: * Initialize all options without values to their defaults. The
0526: * default value for word, file, and attribute options is
0527: * <code>null</code> if no multiple occurrences are allowed and the
0528: * empty list otherwise.
0529: */
0530: public void initDefaultValues() {
0531: for (Option option : optionList) {
0532: if (!options.containsKey(option.internal)) {
0533: Object value = null;
0534:
0535: if (null != option.value) {
0536: if (option.multiple) {
0537: List<Object> list = new ArrayList<Object>(1);
0538: list.add(option.value);
0539: value = list;
0540: } else {
0541: value = option.value;
0542: }
0543: } else if (option.multiple) {
0544: value = new ArrayList<Object>(0);
0545: }
0546:
0547: options.put(option.internal, value);
0548: }
0549: }
0550: }
0551:
0552: /**
0553: * Initialize all boolean options without values to the specified
0554: * value.
0555: *
0556: * @param value The value.
0557: */
0558: public void initFlags(boolean value) {
0559: for (Option option : optionList) {
0560: if ((Option.Kind.BOOLEAN == option.kind)
0561: && (!options.containsKey(option.internal))) {
0562: options.put(option.internal, value);
0563: }
0564: }
0565: }
0566:
0567: /**
0568: * Initialize all boolean options with the specified prefix and
0569: * without values to the specified value.
0570: *
0571: * @param prefix The prefix.
0572: * @param value The value.
0573: */
0574: public void initFlags(String prefix, boolean value) {
0575: for (Option option : optionList) {
0576: if ((Option.Kind.BOOLEAN == option.kind)
0577: && option.internal.startsWith(prefix)
0578: && (!options.containsKey(option.internal))) {
0579: options.put(option.internal, value);
0580: }
0581: }
0582: }
0583:
0584: /**
0585: * Determine whether the specified option has a value.
0586: *
0587: * @param name The internal name.
0588: * @return <code>true</code> if the option has a value.
0589: */
0590: public boolean hasValue(String name) {
0591: return options.containsKey(name);
0592: }
0593:
0594: /**
0595: * Determine whether any option with the specified prefix has a
0596: * value.
0597: *
0598: * @param prefix The prefix.
0599: * @return <code>true</code> if any option with the prefix has a
0600: * value.
0601: */
0602: public boolean hasPrefixValue(String prefix) {
0603: for (String s : options.keySet()) {
0604: if (s.startsWith(prefix))
0605: return true;
0606: }
0607: return false;
0608: }
0609:
0610: /**
0611: * Get the value of the specified option.
0612: *
0613: * @param name The internal name.
0614: * @return The option's value.
0615: * @throws IllegalArgumentException Signals that the option has no
0616: * value.
0617: */
0618: public Object getValue(String name) {
0619: if (options.containsKey(name)) {
0620: return options.get(name);
0621: } else {
0622: throw new IllegalArgumentException(
0623: "Undefined internal option " + name);
0624: }
0625: }
0626:
0627: /**
0628: * Test the value of the specified boolean option.
0629: *
0630: * @param name The internal name.
0631: * @return The option's boolean value.
0632: * @throws IllegalArgumentException Signals that the corresponding
0633: * option has no value.
0634: * @throws ClassCastException Signals that the corresponding option
0635: * does not have a boolean value.
0636: */
0637: public boolean test(String name) {
0638: if (options.containsKey(name)) {
0639: return (Boolean) options.get(name);
0640: } else {
0641: throw new IllegalArgumentException(
0642: "Undefined boolean option " + name);
0643: }
0644: }
0645:
0646: /**
0647: * Get the integer value of the specified option.
0648: *
0649: * @param name The internal name.
0650: * @return The option's integer value.
0651: * @throws IllegalArgumentException Signals that the corresponding
0652: * option has no value.
0653: * @throws ClassCastException Signals that the corresponding option
0654: * does not have an integer value.
0655: */
0656: public int getInt(String name) {
0657: if (options.containsKey(name)) {
0658: return ((Integer) options.get(name)).intValue();
0659: } else {
0660: throw new IllegalArgumentException(
0661: "Undefined integer option " + name);
0662: }
0663: }
0664:
0665: /**
0666: * Get the string value of the specified option.
0667: *
0668: * @param name The internal name.
0669: * @return The option's string value.
0670: * @throws IllegalArgumentException Signals that the corresponding
0671: * option has no value.
0672: * @throws ClassCastException Signals that the corresponding option
0673: * does not have an integer value.
0674: */
0675: public String getString(String name) {
0676: if (options.containsKey(name)) {
0677: return (String) options.get(name);
0678: } else {
0679: throw new IllegalArgumentException("Undefined word option "
0680: + name);
0681: }
0682: }
0683:
0684: /**
0685: * Get the file value of the specified option.
0686: *
0687: * @param name The internal name.
0688: * @return The option's file value.
0689: * @throws IllegalArgumentException Signals that the corresponding
0690: * option has no value.
0691: * @throws ClassCastException Signals that the corresponding option
0692: * does not have a file value.
0693: */
0694: public File getFile(String name) {
0695: if (options.containsKey(name)) {
0696: return (File) options.get(name);
0697: } else {
0698: throw new IllegalArgumentException(
0699: "Undefined file/directory option " + name);
0700: }
0701: }
0702:
0703: /**
0704: * Get the list value of the specified option.
0705: *
0706: * @param name The internal name.
0707: * @return The option's list value.
0708: * @throws IllegalArgumentException Signals that the corresponding
0709: * option has no value.
0710: * @throws ClassCastException Signals that the corresponding option
0711: * does not have a list value.
0712: */
0713: public List<?> getList(String name) {
0714: if (options.containsKey(name)) {
0715: return (List) options.get(name);
0716: } else {
0717: throw new IllegalArgumentException("Undefined option "
0718: + name + " with multiple values");
0719: }
0720: }
0721:
0722: /**
0723: * Get the attribute list value of the specified option.
0724: *
0725: * @param name The internal name.
0726: * @return The option's attribute list value.
0727: * @throws IllegalArgumentException Signals that the corresponding
0728: * option has no value.
0729: * @throws ClassCastException Signals that the corresponding option
0730: * does not have an attribute list value.
0731: */
0732: @SuppressWarnings("unchecked")
0733: public List<Attribute> getAttributeList(String name) {
0734: List<?> l = getList(name);
0735: // Make sure the list actually contains attributes.
0736: if (0 < l.size()) {
0737: @SuppressWarnings("unused")
0738: Attribute a = (Attribute) l.get(0);
0739: }
0740: return (List<Attribute>) l;
0741: }
0742:
0743: /**
0744: * Get the file list value of the specified option.
0745: *
0746: * @param name The internal name.
0747: * @return The option's file list value.
0748: * @throws IllegalArgumentException Signals that the corresponding
0749: * option has no value.
0750: * @throws ClassCastException Signals that the corresponding option
0751: * does not have a file list value.
0752: */
0753: @SuppressWarnings("unchecked")
0754: public List<File> getFileList(String name) {
0755: List<?> l = getList(name);
0756: // Make sure the list actually contains files.
0757: if (0 < l.size()) {
0758: @SuppressWarnings("unused")
0759: File f = (File) l.get(0);
0760: }
0761: return (List<File>) l;
0762: }
0763:
0764: /**
0765: * Check that the specified value is valid for the specified option.
0766: *
0767: * @param option The option.
0768: * @param value The value.
0769: * @throws IllegalArgumentException Signals that the value is
0770: * invalid.
0771: */
0772: protected void check(Option option, Object value) {
0773: switch (option.kind) {
0774: case BOOLEAN:
0775: if (!(value instanceof Boolean)) {
0776: throw new IllegalArgumentException("Invalid value "
0777: + value + " for boolean option "
0778: + option.internal);
0779: }
0780: break;
0781:
0782: case WORD:
0783: if (!(value instanceof String)) {
0784: throw new IllegalArgumentException("Invalid value "
0785: + value + " for word option " + option.internal);
0786: }
0787: break;
0788:
0789: case INTEGER:
0790: if (!(value instanceof Integer)) {
0791: throw new IllegalArgumentException("Invalid value "
0792: + value + " for number option "
0793: + option.internal);
0794: }
0795: break;
0796:
0797: case FILE:
0798: if ((!(value instanceof File))
0799: || (!((File) value).exists())) {
0800: throw new IllegalArgumentException("Invalid value "
0801: + value + " for file option " + option.internal);
0802: }
0803: break;
0804:
0805: case DIRECTORY:
0806: if ((!(value instanceof File))
0807: || (!((File) value).isDirectory())) {
0808: throw new IllegalArgumentException("Invalid value "
0809: + value + " for directory option "
0810: + option.internal);
0811: }
0812: break;
0813:
0814: case ATTRIBUTE:
0815: if (!(value instanceof Attribute)) {
0816: throw new IllegalArgumentException("Invalid value "
0817: + value + " for attribute option "
0818: + option.internal);
0819: }
0820: break;
0821:
0822: default:
0823: assert false : "Invalid option " + option;
0824: }
0825: }
0826:
0827: /**
0828: * Set the value of the specified option.
0829: *
0830: * @param name The internal name.
0831: * @param value The value.
0832: * @throws IllegalArgumentException Signals an unrecognized option
0833: * or an invalid value.
0834: */
0835: public void setValue(String name, Object value) {
0836: Option option = internalMap.get(name);
0837:
0838: if (null == option) {
0839: throw new IllegalArgumentException("Undefined option "
0840: + name);
0841: } else {
0842: check(option, value);
0843:
0844: if (option.multiple) {
0845: List<Object> list = new ArrayList<Object>(1);
0846: list.add(value);
0847: value = list;
0848: }
0849:
0850: options.put(name, value);
0851: }
0852: }
0853:
0854: /**
0855: * Set the value of the specified boolean-valued option.
0856: *
0857: * @param name The internal name.
0858: * @param value The value.
0859: * @throws IllegalArgumentException Signals an unrecognized option
0860: * or not a boolean-valued option.
0861: */
0862: public void setValue(String name, boolean value) {
0863: Option option = internalMap.get(name);
0864:
0865: if (null == option) {
0866: throw new IllegalArgumentException("Undefined option "
0867: + name);
0868:
0869: } else if (Option.Kind.BOOLEAN != option.kind) {
0870: throw new IllegalArgumentException(
0871: "Not a boolean-valued option " + name);
0872:
0873: } else {
0874: options.put(name, value);
0875: }
0876: }
0877:
0878: // ========================================================================
0879:
0880: /**
0881: * Locate the specified file. This method searches this runtime's
0882: * list of input directories.
0883: *
0884: * @see #INPUT_DIRECTORY
0885: *
0886: * @param path The (relative) file path.
0887: * @return The corresponding file.
0888: * @throws FileNotFoundException
0889: * Signals that the specified file could not be found.
0890: */
0891: public File locate(String path) throws FileNotFoundException {
0892: List<File> roots = getFileList(INPUT_DIRECTORY);
0893:
0894: if (null != roots) {
0895: for (File root : roots) {
0896: File file = new File(root, path);
0897: if (file.exists() && file.isFile()) {
0898: return file;
0899: }
0900: }
0901: }
0902:
0903: throw new FileNotFoundException(path + " not found");
0904: }
0905:
0906: /**
0907: * Get a reader for the specified file. The reader uses this
0908: * runtime's input encoding and is buffered.
0909: *
0910: * @see #INPUT_ENCODING
0911: *
0912: * @param file The file.
0913: * @return The corresponding reader.
0914: * @throws IOException Signals an I/O error.
0915: */
0916: public Reader getReader(File file) throws IOException {
0917: return getReader(new FileInputStream(file));
0918: }
0919:
0920: /**
0921: * Get a reader for the specified input stream. The reader uses
0922: * this runtime's input encoding and is buffered.
0923: *
0924: * @see #INPUT_ENCODING
0925: *
0926: * @param in The input stream.
0927: * @return The corresponding reader.
0928: * @throws UnsupportedEncodingException
0929: * Signals that this runtime's encoding is not valid.
0930: */
0931: public Reader getReader(InputStream in)
0932: throws UnsupportedEncodingException {
0933: String encoding = (String) options.get(INPUT_ENCODING);
0934:
0935: if (null == encoding) {
0936: return new BufferedReader(new InputStreamReader(in));
0937: } else {
0938: return new BufferedReader(new InputStreamReader(in,
0939: encoding));
0940: }
0941: }
0942:
0943: /**
0944: * Get this runtime's output directory.
0945: *
0946: * @see #OUTPUT_DIRECTORY
0947: *
0948: * @return The output directory.
0949: */
0950: public File getOutputDirectory() {
0951: return getFile(OUTPUT_DIRECTORY);
0952: }
0953:
0954: /**
0955: * Get a writer for the specified file. The writer uses this
0956: * runtime's output encoding and is buffered.
0957: *
0958: * @see #OUTPUT_ENCODING
0959: *
0960: * @param file The file.
0961: * @return The corresponding writer.
0962: * @throws IOException Signals an I/O error.
0963: */
0964: public Writer getWriter(File file) throws IOException {
0965: return getWriter(new FileOutputStream(file));
0966: }
0967:
0968: /**
0969: * Get a writer for the specified output stream. The writer uses
0970: * this runtime's output encoding and is buffered.
0971: *
0972: * @see #OUTPUT_ENCODING
0973: *
0974: * @param in The output stream.
0975: * @return The corresponding writer.
0976: * @throws UnsupportedEncodingException
0977: * Signals that this runtime's encoding is not valid.
0978: */
0979: public Writer getWriter(OutputStream in)
0980: throws UnsupportedEncodingException {
0981: String encoding = (String) options.get(OUTPUT_ENCODING);
0982:
0983: if (null == encoding) {
0984: return new BufferedWriter(new OutputStreamWriter(in));
0985: } else {
0986: return new BufferedWriter(new OutputStreamWriter(in,
0987: encoding));
0988: }
0989: }
0990:
0991: // ========================================================================
0992:
0993: /**
0994: * Determine whether errors have been reported.
0995: *
0996: * @return <code>true</code> if errors have been reported.
0997: */
0998: public boolean seenError() {
0999: return (0 < errors);
1000: }
1001:
1002: /**
1003: * Get the current error count.
1004: *
1005: * @return The current error count.
1006: */
1007: public int errorCount() {
1008: return errors;
1009: }
1010:
1011: /** Record an error reported through another means. */
1012: public void error() {
1013: errors++;
1014: }
1015:
1016: /**
1017: * Print the specified error message.
1018: *
1019: * @param msg The error message.
1020: */
1021: public void error(String msg) {
1022: errConsole.p("error: ").pln(msg).flush();
1023: errors++;
1024: }
1025:
1026: /**
1027: * Print the specified error message.
1028: *
1029: * @param msg The error message.
1030: * @param n The offending node.
1031: */
1032: public void error(String msg, Node n) {
1033: errConsole.loc(n).p(": ");
1034: error(msg);
1035: }
1036:
1037: /** Record a warning reported through another means. */
1038: public void warning() {
1039: warnings++;
1040: }
1041:
1042: /**
1043: * Print the specified warning message.
1044: *
1045: * @param msg The warning message.
1046: */
1047: public void warning(String msg) {
1048: errConsole.p("warning: ").pln(msg).flush();
1049: warnings++;
1050: }
1051:
1052: /**
1053: * Print the specified warning message.
1054: *
1055: * @param msg The warning message.
1056: * @param n The offending node.
1057: */
1058: public void warning(String msg, Node n) {
1059: errConsole.loc(n).p(": ");
1060: warning(msg);
1061: }
1062:
1063: // ========================================================================
1064:
1065: /**
1066: * Exit the tool. This method terminates the Java virtual machine
1067: * with the appropriate exit code and a summary of error and warning
1068: * numbers if any have been reported.
1069: */
1070: public void exit() {
1071: if (0 < errors) {
1072: if (1 == errors) {
1073: errConsole.p("1 error");
1074: } else {
1075: errConsole.p(errors);
1076: errConsole.p(" errors");
1077: }
1078: }
1079: if (0 < warnings) {
1080: if (0 < errors) {
1081: errConsole.p(", ");
1082: }
1083: if (1 == warnings) {
1084: errConsole.p("1 warning");
1085: } else {
1086: errConsole.p(warnings);
1087: errConsole.p(" warnings");
1088: }
1089: }
1090: if ((0 < errors) || (0 < warnings)) {
1091: errConsole.pln().flush();
1092: }
1093:
1094: if (0 < errors) {
1095: System.exit(1);
1096: } else {
1097: System.exit(0);
1098: }
1099: }
1100:
1101: }
|