001: /*
002: * <copyright>
003: *
004: * Copyright 2001-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: package org.cougaar.tools.csmart.experiment;
027:
028: import org.cougaar.core.agent.AgentManager;
029: import org.cougaar.core.component.ComponentDescription;
030: import org.cougaar.core.plugin.PluginManager;
031: import org.cougaar.tools.csmart.core.cdata.AgentAssetData;
032: import org.cougaar.tools.csmart.core.cdata.AgentComponentData;
033: import org.cougaar.tools.csmart.core.cdata.ComponentData;
034: import org.cougaar.tools.csmart.core.cdata.GenericComponentData;
035: import org.cougaar.tools.csmart.core.cdata.LeafComponentData;
036: import org.cougaar.tools.csmart.core.cdata.PGPropData;
037: import org.cougaar.tools.csmart.core.cdata.PGPropMultiVal;
038: import org.cougaar.tools.csmart.core.cdata.PropGroupData;
039: import org.cougaar.tools.csmart.core.cdata.RelationshipData;
040: import org.cougaar.tools.csmart.core.property.BaseComponent;
041: import org.cougaar.tools.csmart.core.property.name.ComponentName;
042: import org.cougaar.tools.csmart.society.AgentComponent;
043: import org.cougaar.tools.csmart.ui.viewer.CSMART;
044: import org.cougaar.util.log.Logger;
045:
046: import java.io.File;
047: import java.io.FileWriter;
048: import java.io.IOException;
049: import java.io.PrintWriter;
050: import java.text.DateFormat;
051: import java.util.Date;
052: import java.util.Iterator;
053: import java.util.List;
054:
055: // Create a society ComponentData
056: public class ExperimentINIWriter implements ConfigurationWriter {
057: private transient NodeComponent[] nodesToWrite;
058: private transient List components;
059: private ComponentData theSoc;
060: private String trialID = null;
061: private transient Logger log;
062:
063: // Remove when prototypes are fixed.
064: private String metricsInitializer = null;
065:
066: public ExperimentINIWriter(ComponentData theSoc) {
067: this .theSoc = theSoc;
068: createLogger();
069: }
070:
071: public void setTrialID(String id) {
072: this .trialID = id;
073: }
074:
075: private void createLogger() {
076: log = CSMART.createLogger(this .getClass().getName());
077: }
078:
079: public ExperimentINIWriter(List components,
080: NodeComponent[] nodesToWrite, ExperimentBase exp) {
081: createLogger();
082: this .nodesToWrite = nodesToWrite;
083: this .components = components;
084: this .trialID = exp.getTrialID();
085: theSoc = new GenericComponentData();
086: theSoc.setType(ComponentData.SOCIETY);
087: theSoc.setName(exp.getExperimentName()); // this should be experiment: trial FIXME
088: theSoc.setClassName("java.lang.Object"); // leave this out? FIXME
089: theSoc.setOwner(exp); // the experiment
090: theSoc.setParent(null);
091: // For each node, create a GenericComponentData, and add it to the society
092: addNodes(exp);
093:
094: // Some components will want access to the complete set of Nodes in the society, etc.
095: // To get that, they must get back to the root soc object,
096: // and do a getOwner and go from there. Ugly.
097:
098: // Now ask each component in turn to add its stuff
099: for (int i = 0; i < components.size(); i++) {
100: BaseComponent soc = (BaseComponent) components.get(i);
101: soc.addComponentData(theSoc);
102: }
103: // Then give everyone a chance to modify what they've collectively produced
104: // for (int i = components.size() - 1; i >= 0; i--) {
105: for (int i = 0; i < components.size(); i++) {
106: BaseComponent soc = (BaseComponent) components.get(i);
107: soc.modifyComponentData(theSoc);
108: }
109: }
110:
111: private void addNodes(ExperimentBase exp) {
112: for (int i = 0; i < nodesToWrite.length; i++) {
113: ComponentData nc = new GenericComponentData();
114: nc.setType(ComponentData.NODE);
115: nc.setName(nodesToWrite[i].getShortName());
116: nc.setClassName(""); // leave this out?? FIXME
117: nc.setOwner(exp); // the experiment? FIXME
118: nc.setParent(theSoc);
119: ComponentName name = new ComponentName(
120: (BaseComponent) nodesToWrite[i],
121: "ConfigurationFileName");
122: try {
123: nc.addParameter(((BaseComponent) nodesToWrite[i])
124: .getProperty(name).getValue().toString());
125: } catch (NullPointerException e) {
126: nc.addParameter(nc.getName() + ".ini");
127: }
128: theSoc.addChild(nc);
129: addAgents(nodesToWrite[i], nc);
130: }
131: }
132:
133: private void addAgents(NodeComponent node, ComponentData nc) {
134: AgentComponent[] agents = node.getAgents();
135: if (agents == null || agents.length == 0)
136: return;
137: for (int i = 0; i < agents.length; i++) {
138: AgentComponentData ac = new AgentComponentData();
139: ac.setName(agents[i].getFullName().toString());
140: // FIXME!!
141: ac.setOwner(null); // the society that contains this agent FIXME!!!
142: ac.setParent(nc);
143: nc.addChild((ComponentData) ac);
144: }
145: }
146:
147: public void writeConfigFiles(File configDir) throws IOException {
148: // Call writeNodeFile for each of the nodes in theSoc.
149: ComponentData[] nodes = theSoc.getChildren();
150: writeSocietyFile(configDir, theSoc);
151: for (int i = 0; i < theSoc.childCount(); i++) {
152: if (nodes[i].getType().equals(ComponentData.HOST)) {
153: writeHostFile(configDir, nodes[i]);
154: for (int j = 0; j < nodes[i].childCount(); j++) {
155: if (nodes[i].getChildren()[j] instanceof AgentComponentData) {
156: writeAgentFile(configDir,
157: (AgentComponentData) nodes[i]
158: .getChildren()[j]);
159: } else {
160: writeNodeFile(configDir,
161: nodes[i].getChildren()[j]);
162: }
163: }
164: } else {
165: if (nodes[i] instanceof AgentComponentData) {
166: writeAgentFile(configDir,
167: (AgentComponentData) nodes[i]);
168: } else {
169: writeNodeFile(configDir, nodes[i]);
170: }
171: }
172: }
173: }
174:
175: private void writeHostFile(File configDir, ComponentData hc)
176: throws IOException {
177: if (hc.childCount() == 0) {
178: // host has no Nodes. Skip it
179: return;
180: }
181: String configFileName = hc.getName() + ".txt";
182: PrintWriter writer = new PrintWriter(new FileWriter(new File(
183: configDir, configFileName)));
184: try {
185: ComponentData[] children = hc.getChildren();
186: for (int i = 0; i < hc.childCount(); i++) {
187: writer.print(children[i].getType() + " = ");
188: writeChildLine(writer, children[i]);
189: }
190: } catch (Exception e) {
191: if (log.isErrorEnabled()) {
192: log.error("Error writing config file: " + e);
193: }
194: } finally {
195: writer.close();
196: }
197: }
198:
199: private void writeSocietyFile(File configDir, ComponentData hc)
200: throws IOException {
201: String configFileName = hc.getName() + ".txt";
202: PrintWriter writer = new PrintWriter(new FileWriter(new File(
203: configDir, configFileName)));
204: try {
205: writer.print("society = ");
206: writeChildLine(writer, hc);
207: } catch (Exception e) {
208: if (log.isErrorEnabled()) {
209: log.error("Error writing config file: " + e);
210: }
211: } finally {
212: writer.close();
213: }
214: }
215:
216: private String writeParam(Object param) {
217: // do fancy stuff based on type here??? FIXME!!
218: return param.toString();
219: }
220:
221: private static String quote(String string) {
222: return "\"" + string + "\"";
223: }
224:
225: private void writeChildLine(PrintWriter writer, ComponentData me) {
226: if (me.getType().equals(ComponentData.SOCIETY)
227: || me.getType().equals(ComponentData.AGENT)
228: || me.getType().equals(ComponentData.HOST)
229: || me.getClassName() == null)
230: writer.print(me.getName());
231: else
232: writer.print(me.getClassName());
233: if (me.parameterCount() == 0
234: || me.getType().equals(ComponentData.AGENT)) {
235: writer.println();
236: return;
237: }
238: writer.print("(");
239: Object[] params = me.getParameters();
240: writer.print(writeParam(params[0]));
241: for (int i = 1; i < params.length; i++) {
242: writer.print(",");
243: // write out each parameter, comma separated
244: writer.print(writeParam(params[i]));
245: }
246:
247: // HACK!! Show how to make these INI files runnable
248: boolean isGLS = false;
249: if (me.getClassName().indexOf("GLSInitServlet") != -1) {
250: isGLS = true;
251: // it looks like it could get the org.cougaar.experiment.id from
252: // its parameters list, so write out another parameter
253: // the trialid
254: // if (trialID != null)
255: // writer.print(",exptid=" + trialID);
256: }
257:
258: writer.println(")");
259: if (isGLS && trialID != null)
260: writer
261: .println("# To make INI files MAYBE runnable, add the following parameter to the GLSInitServlet: exptid="
262: + trialID);
263: return;
264: }
265:
266: private void writeLeafData(File configDir, ComponentData me)
267: throws IOException {
268: if (me.leafCount() < 1)
269: return;
270: LeafComponentData[] leaves = me.getLeafComponents();
271: for (int i = 0; i < me.leafCount(); i++) {
272: LeafComponentData leaf = leaves[i];
273: if (leaf == null)
274: continue;
275: if (!leaf.getType().equals(LeafComponentData.FILE)) {
276: if (log.isErrorEnabled()) {
277: log.error("Got unknown LeafComponent type: "
278: + leaf.getType());
279: }
280: continue;
281: }
282: PrintWriter writer = new PrintWriter(new FileWriter(
283: new File(configDir, leaf.getName())));
284: try {
285: writer.println(leaf.getValue().toString());
286: } catch (Exception e) {
287: if (log.isErrorEnabled()) {
288: log.error("Error writing config file: ", e);
289: }
290: } finally {
291: writer.close();
292: }
293: } // end of loop over leaves
294: } // end of writeLeafData
295:
296: private void writeNodeFile(File configDir, ComponentData nc)
297: throws IOException {
298: if (nc.childCount() == 0) {
299: if (log.isDebugEnabled()) {
300: log.debug("Found node with no children. it's parent: "
301: + nc.getParent());
302: }
303: // node has no agents or binders.
304: // If it also has no Parent (IE the parent is the society, not a host)
305: // then skip it
306: if (nc.getParent() == null
307: || nc.getParent().getType().equals(
308: ComponentData.SOCIETY))
309: return;
310: } else if (nc.getParent() == null
311: || nc.getParent().getType().equals(
312: ComponentData.SOCIETY)) {
313: // Node has children, but is not assigned to a Host.
314: // Maybe we're dumping a raw society, not assigned to resources. In this case,
315: // writing is OK. But if the Node has no Agents, maybe we should
316: // not write out?
317: }
318:
319: Object[] parameters = nc.getParameters();
320: String configFileName = nc.getName() + ".ini";
321: if (parameters.length > 0)
322: configFileName = (String) parameters[0];
323: if (!configFileName.endsWith(".ini"))
324: configFileName = configFileName + ".ini";
325: PrintWriter writer = new PrintWriter(new FileWriter(new File(
326: configDir, configFileName)));
327:
328: try {
329: // loop over children
330: // if there are binder or such, do those
331: ComponentData[] children = nc.getChildren();
332: for (int i = 0; i < nc.childCount(); i++) {
333: if (children[i] instanceof AgentComponentData) {
334: continue;
335: } else if (children[i].getType().equals(
336: ComponentData.NODE)
337: || children[i].getType().equals(
338: ComponentData.SOCIETY)
339: || children[i].getType().equals(
340: ComponentData.AGENT)) {
341: if (log.isErrorEnabled()) {
342: log.error("Got unexpected child of Node type: "
343: + children[i].getType());
344: }
345: } else {
346: if (log.isDebugEnabled()) {
347: log.debug("writeNodeFile: [ " + configFileName
348: + " ] Type is: "
349: + children[i].getType());
350: }
351: // What is the prefix line I write here?
352: if (children[i].getType().equals(
353: ComponentData.NODEBINDER)) {
354: writer.print(AgentManager.INSERTION_POINT
355: + ".Binder");
356: } else if (children[i].getType().equals(
357: ComponentData.AGENTBINDER)) {
358: writer.print(PluginManager.INSERTION_POINT
359: + ".Binder");
360: } else {
361: if (log.isDebugEnabled()) {
362: log
363: .debug("writeNodeFile writing component of type: "
364: + children[i].getType()
365: + ", and name "
366: + children[i].getName());
367: }
368: writer.print(children[i].getType());
369: // This assumes the type is always the prefix.
370: }
371: if (ComponentDescription.parsePriority(children[i]
372: .getPriority()) != ComponentDescription.PRIORITY_COMPONENT) {
373: writer.print("(" + children[i].getPriority()
374: + ")");
375: }
376: writer.print(" = ");
377: writeChildLine(writer, children[i]);
378: // Could one of these guys have children?
379: writeChildrenOfComp(writer, configDir, children[i]);
380: // write out any leaf components
381: writeLeafData(configDir, children[i]);
382: }
383: } // end of loop over children
384: writer.println("[ Clusters ]");
385: children = nc.getChildren();
386:
387: // Get the name if the initializer plugin.
388: for (int i = 0; i < nc.childCount(); i++) {
389: if (children[i] instanceof AgentComponentData) {
390: AgentComponentData agent = (AgentComponentData) children[i];
391: ComponentData[] plugins = agent.getChildren();
392: for (int j = 0; j < plugins.length; j++) {
393: ComponentData aa = plugins[j];
394: if (aa
395: .getName()
396: .equals(
397: "org.cougaar.tools.csmart.runtime.plugin.MetricsInitializerPlugin")) {
398: metricsInitializer = agent.getName()
399: .toString();
400: break;
401: }
402: }
403: }
404: }
405:
406: for (int i = 0; i < nc.childCount(); i++) {
407: if (children[i] instanceof AgentComponentData) {
408: writer.print("cluster = ");
409: writeChildLine(writer, children[i]);
410: // Write the children of this agent if there are any
411: // write the leaf components of this agent
412: writeAgentFile(configDir,
413: (AgentComponentData) children[i]);
414: } else if (children[i].getType().equals(
415: ComponentData.NODEBINDER)) {
416: } else {
417: // } else if (!children[i].getType().equals(ComponentData.NODEBINDER)
418: // || !children[i].getType().equals(ComponentData.AGENTBINDER)) {
419: if (log.isDebugEnabled()) {
420: log
421: .debug("Got a child of a Node that wasn't an Agent or Node Binder Type: "
422: + children[i].getType());
423: }
424: }
425: }
426: writer.println();
427: writer.println("[ AlpProcess ]");
428: writer.println();
429: writer.println("[ Policies ]");
430: writer.println();
431: writer.println("[ Permission ]");
432: writer.println();
433: writer.println("[ AuthorizedOperation ]");
434: } catch (Exception e) {
435: if (log.isErrorEnabled()) {
436: log.error("Error writing config file: ", e);
437: }
438: } finally {
439: writer.close();
440: }
441: } // end of writeNodeFile
442:
443: private void writeChildrenOfComp(PrintWriter writer,
444: File configDir, ComponentData comp) throws IOException {
445: if (comp == null || comp.childCount() == 0)
446: return;
447:
448: ComponentData[] children = comp.getChildren();
449: for (int i = 0; i < children.length; i++) {
450: if (writer != null) {
451: if (children[i].getType().equals(
452: ComponentData.AGENTBINDER)) {
453: writer.print(PluginManager.INSERTION_POINT
454: + ".Binder");
455: } else if (children[i].getType().equals(
456: ComponentData.NODEBINDER)) {
457: writer.print(AgentManager.INSERTION_POINT
458: + ".Binder");
459: } else {
460: writer.print(children[i].getType());
461: }
462: if (ComponentDescription.parsePriority(children[i]
463: .getPriority()) != ComponentDescription.PRIORITY_COMPONENT) {
464: writer.print("(" + children[i].getPriority() + ")");
465: }
466: writer.print(" = ");
467: writeChildLine(writer, children[i]);
468: }
469: // Could one of these guys have children?
470: writeChildrenOfComp(writer, configDir, children[i]);
471: // write out any leaf components
472: writeLeafData(configDir, children[i]);
473: }
474: }
475:
476: private DateFormat myDateFormat = DateFormat.getInstance();
477:
478: private void writeAgentFile(File configDir, AgentComponentData ac)
479: throws IOException {
480: PrintWriter writer = new PrintWriter(new FileWriter(new File(
481: configDir, ac.getName() + ".ini")));
482: try {
483: writer.println("[ Cluster ]");
484: writer.println("class = " + ac.getClassName());
485: AgentAssetData aad = ac.getAgentAssetData();
486: writer.println("uic = "
487: + ((aad == null) ? "UIC/" + ac.getName() : aad
488: .getUIC()));
489: writer.println("cloned = false");
490: writer.println();
491: writer.println("[ Plugins ]");
492: writer
493: .println("# Note, this society may not run correctly because OrtRTDataPlugin may");
494: writer
495: .println("# have been changed to OrgDataPlugin on loading from INI files");
496: // loop over the children - but what if one is not a PLUGIN?
497: // This does no type checking, and writes out all the children here
498: // if(log.isDebugEnabled()) {
499: // log.debug("Agent: " + ac.getName() + " has " + ac.childCount() + " Children");
500: // }
501: writeChildrenOfComp(writer, configDir, (ComponentData) ac);
502: writer.println();
503: writer.println("[ Policies ]");
504: writer.println();
505: writer.println("[ Permission ]");
506: writer.println();
507: writer.println("[ AuthorizedOperation ]");
508: } catch (Exception e) {
509: if (log.isErrorEnabled()) {
510: log.error("Error writing config file: ", e);
511: }
512: } finally {
513: writer.close();
514: }
515: // write the prototype-ini file
516: writePrototypeINI(configDir, ac);
517:
518: // write any other leaf component data files
519: writeLeafData(configDir, (ComponentData) ac);
520:
521: } // end of writeAgentFile
522:
523: private void writePrototypeINI(File configDir,
524: AgentComponentData agent) throws IOException {
525: AgentAssetData assetData = agent.getAgentAssetData();
526:
527: if (assetData == null) {
528: // No Asset info available
529: return;
530: }
531:
532: PrintWriter writer = new PrintWriter(new FileWriter(new File(
533: configDir, agent.getName() + "-prototype-ini.dat")));
534: try {
535: writer
536: .println("# Asset Class may have been modified on parse!");
537: writer.print("[Prototype] ");
538: writer.println(assetData.getAssetClass());
539: writer.println();
540:
541: // if(!assetData.isEntity()) {
542: if (assetData.getUniqueID() != null
543: && !assetData.getUniqueID().equals("")) {
544: writer.print("[UniqueId] ");
545: writer.println(quote(assetData.getUniqueID()));
546: writer.println();
547: }
548:
549: if (assetData.getUnitName() != null
550: && !assetData.getUnitName().equals("")) {
551: writer.print("[UnitName] ");
552: writer.println(assetData.getUnitName());
553: writer.println();
554: }
555:
556: if (!assetData.isNewIniFormat()) {
557: writer.print("[UIC] ");
558: writer.println(quote(assetData.getUIC()));
559: writer.println();
560: }
561: // }
562:
563: // Write Relationships.
564: Iterator iter = assetData.getRelationshipIterator();
565: writer.println("[Relationship]");
566: while (iter.hasNext()) {
567: RelationshipData rel = (RelationshipData) iter.next();
568:
569: if (assetData.isNewIniFormat()) {
570: if (log.isInfoEnabled()) {
571: log.info("Writing out new INI file format");
572: }
573: if (assetData.isEntity()) {
574: // AMH: Must write not the Type but the role if
575: // the type says Supporting
576: if (rel.getType().equals("Supporting"))
577: writer.print(quote(rel.getRole()) + " ");
578: else
579: writer.print(rel.getType() + " ");
580: writer.print(quote(rel.getItemId()) + " ");
581: writer.print(quote(rel.getTypeId()) + " ");
582: writer.print(quote(rel.getSupported()) + " ");
583: long startTime = rel.getStartTime();
584: long endTime = rel.getEndTime();
585: if (startTime == 0L || endTime == 0L) {
586: writer.print(quote("") + " ");
587: writer.println(quote(""));
588: } else {
589: writer.print(quote(myDateFormat
590: .format(new Date(startTime)))
591: + " ");
592: writer.println(quote(myDateFormat
593: .format(new Date(endTime))));
594: }
595: } else if (assetData.isOrg()) {
596: writer.print(rel.getType() + " ");
597: writer.print(quote(rel.getSupported()) + " ");
598: writer.println(quote(rel.getRole()));
599: } else if (assetData.isTPOrg()) {
600: // To Do: Deals with Relationship.ini file
601: // FIXME!!!!!
602: } else {
603: throw new RuntimeException(
604: "Asset Data Type Must be set: Entity, Org or TPOrg");
605: }
606: } else {
607: if (log.isInfoEnabled()) {
608: log.info("Writing out old INI file format");
609: }
610:
611: if (rel.getRole().equalsIgnoreCase("Subordinate")) {
612: writer.print("Superior ");
613: writer.print(quote(rel.getSupported()) + " ");
614: writer.println(quote(""));
615: } else {
616: writer.print(rel.getType() + " ");
617: writer.print(quote(rel.getSupported()) + " ");
618: writer.println(quote(rel.getRole()) + " ");
619: }
620: }
621: }
622: writer.println();
623:
624: iter = assetData.getPropGroupsIterator();
625: while (iter.hasNext()) {
626: PropGroupData pgData = (PropGroupData) iter.next();
627:
628: writer.println("[" + pgData.getName() + "]");
629: Iterator iter2 = pgData.getPropertiesIterator();
630: while (iter2.hasNext()) {
631: PGPropData propData = (PGPropData) iter2.next();
632: if (propData.getName().equals("HomeLocation")) {
633: writer
634: .println("# If this society was read from INI files,");
635: writer
636: .println("# the parser may have removed data from this field that");
637: writer
638: .println("# will prevent running from this INI file.");
639: writer
640: .println("# Refer to an older ini file for the missing info.");
641: }
642:
643: writer.print(propData.getName() + " ");
644:
645: // This is ugly!
646: if (propData.getName().equals("HomeLocation")) {
647: writer.print("GeolocLocation");
648: } else if (propData.getType().equals("COLLECTION")) {
649: writer.print("Collection");
650: } else if (propData.getType().equals("LIST")) {
651: writer.print("List");
652: } else {
653: writer.print(propData.getType());
654: }
655:
656: if (propData.isListType()) {
657: writer.print("<" + propData.getSubType() + ">");
658: writer.print(" "
659: + quote(((PGPropMultiVal) propData
660: .getValue()).toString()));
661: writer.println();
662: } else {
663: writer.println(" "
664: + quote((String) propData.getValue()));
665: }
666: }
667: writer.println();
668: }
669:
670: } finally {
671: writer.close();
672: }
673: }
674:
675: public String toString() {
676: StringBuffer buf = new StringBuffer();
677: buf.append("ExperimentINIWriter: ");
678: buf.append(theSoc.toString());
679: return buf.toString();
680: }
681:
682: } // end of ExperimentINIWriter.java
|