001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.core.node;
028:
029: import java.io.BufferedReader;
030: import java.io.FileInputStream;
031: import java.io.IOException;
032: import java.io.InputStream;
033: import java.io.InputStreamReader;
034: import java.io.PrintStream;
035: import java.util.ArrayList;
036: import java.util.List;
037: import java.util.Collections;
038: import java.util.regex.Matcher;
039: import java.util.regex.Pattern;
040:
041: import org.cougaar.core.agent.Agent;
042: import org.cougaar.core.component.ComponentDescription;
043: import org.cougaar.core.plugin.PluginBase;
044: import org.cougaar.util.StringUtility;
045: import org.cougaar.util.CSVUtility;
046:
047: /**
048: * Parse an INI stream into an <code>ComponentDescription[]</code>.
049: */
050: public final class INIParser {
051:
052: private INIParser() {
053: // no need for a constructor -- these are static utility methods
054: }
055:
056: public static ComponentDescription[] parse(String filename) {
057: try {
058: return parse(new FileInputStream(filename));
059: } catch (Exception e) {
060: System.err.println(e);
061: return null;
062: }
063: }
064:
065: public static ComponentDescription[] parse(InputStream in) {
066: return parse(new BufferedReader(new InputStreamReader(in)));
067: }
068:
069: /** Pattern for matching insertionPoint & priority */
070: final static Pattern namePattern = Pattern
071: .compile("([a-zA-Z0-9.]+)(\\((.+)\\))?");
072:
073: /**
074: * Read an INI file from a <code>BufferedReader</code> to create
075: * <code>ComponentDescription</code>s.
076: *
077: * Expected format is:<pre>
078: * # option comment lines and empty lines
079: * [ ignored section headers ]
080: * insertionPoint = classname
081: * # or specify String arguments, which are saved as a List "param"
082: * insertionPoint = classname(arg0, arg1, argN)
083: * # more "insertionPoint = " lines
084: * </pre> and is parsed into an array of ComponentDescriptions.
085: * <p>
086: * These fields are not currently supported:<br>
087: * codebase, certificate, lease, policy
088: * <p>
089: * Two insertion points have special backwards-compatability<ul>
090: * <li>"cluster" == "Node.AgentManager.Agent"</li>
091: * <li>"plugin" == "Node.AgentManager.Agent.PluginManager.Plugin"</li>
092: * </ul>
093: * <p>
094: * Any insertion point (including aliases) may be postfixed with "(<em>PRIORITY</em>)"
095: * where "<em>PRIORITY</em>" is one of the component priority names specified in
096: * ComponentDescription (e.g. HIGH, INTERNAL, BINDER, COMPONENT, LOW, or STANDARD). Note that
097: * the name must be in upper case, no punctuation and no extra whitespace. An example
098: * is: "plugin(LOW)=org.cougaar.test.MyPlugin", which would specify that MyPlugin should be
099: * loaded as a low priority plugin (e.g. after most other plugins and internal components).
100: * <p>
101: * Also "cluster=name" and is converted into<pre>
102: * "Node.AgentManager.Agent=org.cougaar.core.agent.SimpleAgent(name)"
103: * </pre> as a default classname.
104: * <p>
105: * These old "cluster" values are <u>ignored</u>:<ul>
106: * <li>"class" (now specified in the Node's INI!)</li>
107: * <li>"uic" (ignored)</li>
108: * <li>"cloned" (ignored)</li>
109: * </ul>
110: * <br>
111: * <em>Note:</em> This is used by the file-based <code>ComponentInitializerService</code>,
112: * which is declared to return only items <em>below</em> the given insertion point.
113: * However, this method returns <em>all</em> items, regardless
114: * of insertion point. Callers must beware they may get themselves.
115: * @see org.cougaar.core.component.ComponentDescription
116: */
117: public static ComponentDescription[] parse(BufferedReader in) {
118: List descs = new ArrayList();
119: int line = 0;
120: try {
121:
122: readDescriptions: while (true) {
123:
124: // read an entry
125: String s = null;
126: while (true) {
127: // read the current line
128: line++;
129: String tmp = in.readLine();
130: if (tmp == null) {
131: // end of file
132: if (s != null) {
133: System.err
134: .println("Warning: INI file ends in ignored \""
135: + s + "\"");
136: }
137: break readDescriptions;
138: }
139: if (tmp.endsWith("\\")) {
140: tmp = tmp.substring(0, tmp.length() - 1);
141: if (!(tmp.endsWith("\\"))) {
142: // line continuation
143: s = ((s != null) ? (s + tmp) : tmp);
144: continue;
145: }
146: }
147: // finished line
148: s = ((s != null) ? (s + tmp) : tmp);
149: break;
150: }
151:
152: s = s.trim();
153: int eqIndex;
154: if ((s.length() == 0) || (s.startsWith("#"))
155: || ((eqIndex = s.indexOf("=")) <= 0)) {
156: // ignore empty lines, "#comments", and non-"name=value" lines
157: continue;
158: }
159:
160: String name = s.substring(0, eqIndex).trim();
161: String value = s.substring(eqIndex + 1).trim();
162:
163: // special case name/value pairs
164: if (name.equals("name") || name.equals("class")
165: || name.equals("uic") || name.equals("cloned")) {
166: // ignore!
167: continue;
168: }
169:
170: // name is the insertion point
171: String insertionPoint = name;
172: int priority = -1; // illegal priority (undefined) - we'll default it later
173: {
174: Matcher m = namePattern.matcher(insertionPoint);
175: if (m.matches()) {
176: // group 1 is the new name
177: String n = m.group(1);
178: if (n != null)
179: insertionPoint = n;
180:
181: // group 3 is the priority or null
182: String p = m.group(3);
183: if (p != null) {
184: try {
185: priority = ComponentDescription
186: .parsePriority(p);
187: } catch (IllegalArgumentException iae) {
188: System.err
189: .println("Warning: illegal component priority line "
190: + line + ": " + s);
191: }
192: }
193: } else {
194: System.err
195: .println("Warning: unparsable component description line "
196: + line + ": " + s);
197: }
198: }
199:
200: // FIXME only a simplistic property of "List<String>" is supported
201: //
202: // parse value into classname and optional parameters
203: String classname;
204: List vParams = null;
205: int p1;
206: int p2;
207: if (((p1 = value.indexOf('(')) > 0)
208: && ((p2 = value.lastIndexOf(')')) > p1)) {
209: classname = value.substring(0, p1);
210: vParams = CSVUtility.parseToList(value.substring(
211: (p1 + 1), p2));
212: } else {
213: classname = value;
214: }
215:
216: // fix the insertion point for backwards compatibility
217: if (insertionPoint.equals("plugin")) {
218: // should load into an Agent
219: insertionPoint = PluginBase.INSERTION_POINT;
220: } else if (insertionPoint.equals("cluster")) {
221: if (vParams == null) {
222: // fix "cluster=name" to be "cluster=classname(name)"
223: vParams = new ArrayList(1);
224: vParams.add(classname);
225: classname = "org.cougaar.core.agent.SimpleAgent";
226: }
227: // should load into a Node
228: insertionPoint = Agent.INSERTION_POINT;
229: }
230:
231: if (insertionPoint.startsWith(".")) {
232: System.err
233: .println("Warning: insertionPoint starts with \".\" on line "
234: + line + ": " + s);
235: continue;
236: }
237:
238: if (priority == -1) {
239: // default binders to PRIORITY_BINDER instead of STANDARD
240: if (insertionPoint.endsWith(".Binder")) {
241: priority = ComponentDescription.PRIORITY_BINDER;
242: } else {
243: priority = ComponentDescription.PRIORITY_COMPONENT;
244: }
245: }
246:
247: if (vParams != null) {
248: vParams = Collections.unmodifiableList(vParams);
249: }
250: // FIXME unsupported fields: codebase, certificate, lease, policy
251: //
252: // create a new ComponentDescription
253: ComponentDescription cd = new ComponentDescription(
254: classname, // name
255: insertionPoint, classname, null, // codebase
256: vParams, null, // certificate
257: null, // lease
258: null, // policy
259: priority // priority, of course.
260: );
261:
262: // save
263: descs.add(cd);
264: }
265: in.close();
266: } catch (IOException ioe) {
267: System.err.println("Error: " + ioe);
268: }
269:
270: return (ComponentDescription[]) descs
271: .toArray(new ComponentDescription[descs.size()]);
272: }
273:
274: /**
275: * Write an "INI" file from the given <code>ComponentDescription[]</code>.
276: *
277: * @throws ClassCastException if the ComponentDescription[] contains
278: * illegal elements.
279: */
280: public static void write(PrintStream out,
281: ComponentDescription[] descs) {
282: out.print("# machine-generated ini file\n");
283: int ndescs = ((descs != null) ? descs.length : 0);
284: out.print("# " + ndescs + " component"
285: + ((ndescs > 1) ? "s" : "") + ":\n");
286: for (int i = 0; i < ndescs; i++) {
287: ComponentDescription descI = descs[i];
288: out.print(descI.getInsertionPoint());
289: out.print("=");
290: out.print(descI.getClassname());
291: List vParams = (List) descI.getParameter();
292: int nvParams = vParams.size();
293: if (nvParams > 0) {
294: out.print("(");
295: int k = 0;
296: out.print((String) vParams.get(k));
297: while (++k < nvParams) {
298: out.print(", ");
299: out.print((String) vParams.get(k));
300: }
301: out.print(")");
302: }
303: out.print("\n");
304: }
305: }
306:
307: public static void main(String[] args) {
308: System.out.println("testme!");
309: String fname = args[0];
310: System.out.println("read: " + fname);
311: ComponentDescription[] descs = parse(fname);
312: System.out.println("write to stdout");
313: write(System.out, descs);
314: }
315:
316: }
|