+Fri Mar 02 16:08:00 GMT 2001 peter@retep.org.uk
+ - Started importing in the rest of the retep tools.
+
Tue Jan 23 10:19:00 GMT 2001 peter@retep.org.uk
- - Finished the XML Export classes
+ - Finished the XML Export classes
- First of the test data suite now in CVS.
+Retep Tools Implementation
+--------------------------
+
+
+The tools are designed to be put into a single jar file, but each one is
+executable either individually or part of one single application.
+
+To run the big application, you can either:
+
+ java -jar retepTools.jar
+
+or with the retepTools.jar in the classpath run:
+
+ java uk.org.retep.tools.Main
+
+Windows users: For you you can also double click the retepTools.jar as windows
+will automatically run javac for you.
+
+To run the individual tools, you must have the .jar file in your classpath and
+then run the relevant Main class.
+
+Tool Type Class
+------------------------------------------------------------------------------
+pg_hba.conf Editor/repairer Editor uk.org.retep.util.hba.Main
+Properties Editor Editor uk.org.retep.util.proped.Main
+
+
+Layout of the classes
+---------------------
+
+Simply, tools that work on property files (Java properties, resource files,
+configuration settings - pg_hba.conf for example) go under uk.org.retep.util in
+their own package. Other utility classes (like PropertyIO) go in to the
+uk.org.retep.util.misc package except for certain ones where they are related.
+
+ie: TableModels. In swing you have JTable which uses a TableModel to display
+(and possibly update) some data. These go under uk.org.retep.util.models where
+you will find PropertiesTableModel for example. This one allows a Properties
+object to be displayed & updated.
+
+Come core classes like Logger, ExceptionDialog etc go into the main
+uk.org.retep.util package.
+
+Directory/Package Contents
+------------------------------------------------------------------------------
+uk.org.retep Home of the tools.properties file
+uk.org.retep.tools The main all-in-one application
+uk.org.retep.dtu The Data Transform Unit
+uk.org.retep.util Core utility classes
+uk.org.retep.util.hba pg_hba.conf editor/repairer
+uk.org.retep.util.misc Misc utility classes
+uk.org.retep.util.models Swing table models
+uk.org.retep.util.proped Property Editor
+uk.org.retep.util.xml.core Basic XML Factory
+uk.org.retep.util.xml.jdbc JDBC/XML interface
+uk.org.retep.util.xml.parser Simple SAX parser
+
+Structure of a tool
+-------------------
+
+Each tool has at least 2 base classes, and an entry in the tools.properties
+file. For this example, I'll show you the Properties Editor:
+
+Base package uk.org.retep.util.proped
+Main tool class uk.org.retep.util.proped.PropertyEditor
+Standalone class uk.org.retep.util.proped.Main
+
+The main tool class is the entry point used by the main application. Because
+they are used in a GUI, this class must extend javax.swing.JComponent and
+implement the uk.org.retep.tools.Tool interface. (NB: You will find I always
+use JPanel, but JComponent is used here so that any swing class can be used
+you are not limited to JPanel.)
+
+The standalone class is a basic static class that implements the main method.
+It should extend the uk.org.retep.misc.StandaloneApp class and be written along
+the lines of the following example:
+
+ import uk.org.retep.util.StandaloneApp;
+ import javax.swing.JComponent;
+
+ public class Main extends StandaloneApp
+ {
+ public Main(String[] args)
+ throws Exception
+ {
+ super(args);
+ }
+
+ public JComponent init()
+ throws Exception
+ {
+ // Your initialisation here. In this case the PropertyEditor
+ PropertyEditor panel = new PropertyEditor();
+
+ // do stuff here, ie load a file if supplied
+
+ // return the tool
+ return panel;
+ }
+
+ public static void main(String[] args)
+ throws Exception
+ {
+ Main main = new Main(args);
+ main.pack();
+ main.setVisible(true);
+ }
+ }
+
+you will find a template in the uk.org.retep.util.Main class. Simply copy this
+classes source, as it gives you the basic stub. Just add your own implementation
+if init() like the one above. Look at the full Main class for the
+PropertiesEditor to see how to get at the command line args.
+
+By convention, the standalone class is named Main.
+
+Before you ask what retepTools are, they are my personal suite of utilities.
+About 90% of them are JDBC related (either they use JDBC, or I use them in
+developing the JDBC driver).
+
+Now, because of various reasons I won't go into now, in January 2001 I decided
+to release the entire lot to the public. I could have used something like
+SourceForge, but as they are mainly JDBC related I thought here is the best
+place.
+
+Now all (bar retepPDF, see end-note) will over the next few months be going
+into the /contrib/retep directory. They range from simple XML Inport/Export
+classes to entire sub-systems that can be plugged into applications.
+
+All this lot were never released, so I'm placing them under PostgreSQL's
+licence.
+
+Please refer to Implementation for details of what package does what.
+
+It all requires Java2SE (JDK1.2) as a minimum. I do have some plans for some
+EJB tools later, so those will need Java2EE, but not yet ;-)
+
+Peter Mount
+peter@retep.org.uk
+March 2 2001
+
+retepPDF: This is not included for two reasons:
+
+1: It's big and not really related in any way to PostgreSQL
+2: More importantly, I (may be foolishly) released it some 3 years ago under
+ the LGPL. As a few people have added to it, it's not really possible to
+ change the licence, and I don't want to polute PostgreSQL's source tree ;-)
+
+retepGraph: This was an old graphics library. It's been obsolete for 3 years
+now, so it's not going in.
+
build file to build the donated retep tools packages
- $Id: build.xml,v 1.2 2001/01/23 10:22:18 peter Exp $
+ $Id: build.xml,v 1.3 2001/03/05 09:15:35 peter Exp $
-->
</target>
<!-- Builds the XML Tools -->
- <target name="xml" depends="checks,prepare" if="xml">
+ <target name="compile" depends="checks,prepare">
<javac srcdir="${src}" destdir="${dest}">
- <include name="${package}/xml/**" />
+ <include name="${package}/**" />
</javac>
</target>
<!-- Builds the various jar files -->
- <target name="jar" depends="xml">
+ <target name="jar" depends="compile">
<jar jarfile="${jars}/retepTools.jar" basedir="${dest}">
- <include name="${package}/xml/**" if="xml" />
+ <include name="${package}/**" />
</jar>
</target>
<project>\r
<property category="idl" name="ProcessIDL" value="false" />\r
<property category="runtime.0" name="RunnableType" value="com.borland.jbuilder.runtime.ApplicationRunner" />\r
+ <property category="runtime.0" name="application.class" value="uk.org.retep.util.hba.Main" />\r
+ <property category="runtime.0" name="application.parameters" value="-d2 pg_hba.conf" />\r
+ <property category="runtime.0" name="appserver.ejbJarsSaved" value="1" />\r
+ <property category="runtime.0" name="appserver.parameters" value="-jts -jns -jss -jdb" />\r
+ <property category="runtime.0" name="appserver.servername" value="ejbcontainer" />\r
+ <property category="runtime.0" name="appserver.vmparameters" value="" />\r
<property category="runtime.0" name="jsprunner.docbase" value="." />\r
<property category="runtime.0" name="jsprunner.jspfile" value="E%|/docs/java/xml/example6" />\r
<property category="sys" name="AuthorLabel" value="@author" />\r
<property category="sys" name="CompanyLabel" value="Company:" />\r
<property category="sys" name="Copyright" value="Copyright (c) 2001" />\r
<property category="sys" name="CopyrightLabel" value="Copyright:" />\r
- <property category="sys" name="DefaultPackage" value="uk.org.retep.xml.jdbc" />\r
+ <property category="sys" name="DefaultPackage" value="uk.org.retep.util.misc" />\r
<property category="sys" name="Description" value="" />\r
<property category="sys" name="DescriptionLabel" value="Description:" />\r
<property category="sys" name="DocPath" value="doc" />\r
<property category="sys" name="InstanceVisibility" value="0" />\r
<property category="sys" name="JDK" value="java 1.3.0-C" />\r
<property category="sys" name="LastTag" value="0" />\r
- <property category="sys" name="Libraries" value="JAXP;Oracle JDBC" />\r
+ <property category="sys" name="Libraries" value="JAXP;Oracle JDBC;JDK1.3 JRE" />\r
<property category="sys" name="MakeStable" value="0" />\r
<property category="sys" name="OutPath" value="build" />\r
<property category="sys" name="SourcePath" value="." />\r
<property category="sys" name="Version" value="1.0" />\r
<property category="sys" name="VersionLabel" value="@version" />\r
<property category="sys" name="WorkingDirectory" value="." />\r
- <node type="Folder" name="core" />\r
+ <node type="Package" name="uk.org.retep.app" />\r
+ <node type="Package" name="uk.org.retep.dtu" />\r
+ <node type="Package" name="uk.org.retep.tools" />\r
+ <node type="Package" name="uk.org.retep.util" />\r
<node type="Package" name="uk.org.retep.xml.core" />\r
<node type="Package" name="uk.org.retep.xml.jdbc" />\r
<node type="Package" name="uk.org.retep.xml.parser" />\r
<file path="build.xml" />\r
<file path="CHANGELOG" />\r
<file path="Implementation" />\r
+ <file path="uk/org/retep/util/models/PropertiesTableModel.java" />\r
<file path="README" />\r
</project>\r
\r
--- /dev/null
+package uk.org.retep.dtu;
+
+import uk.org.retep.xml.core.XMLFactory;
+import uk.org.retep.xml.core.XMLFactoryException;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Iterator;
+
+public class DCollection implements Collection
+{
+ protected int num,max,inc;
+
+ protected DElement elements[];
+
+ public DCollection()
+ {
+ this(10);
+ }
+
+ public DCollection(int aIncrement)
+ {
+ num=0;
+ max=0;
+ inc=aIncrement;
+ elements=null;
+ }
+
+ protected void resize()
+ {
+ if(num>=max) {
+ max+=inc;
+ DElement n[] = new DElement[max];
+ if(elements!=null) {
+ System.arraycopy(elements,0,n,0,elements.length);
+ }
+ elements=n;
+ }
+ }
+
+ public int size()
+ {
+ return num;
+ }
+
+ public boolean isEmpty()
+ {
+ return (num==0);
+ }
+
+ /**
+ * Checks the list using it's XML id.
+ */
+ public synchronized boolean contains(Object parm1)
+ {
+ if(parm1 instanceof DElement) {
+ DElement e = (DElement) parm1;
+ int ei = e.getID();
+
+ // out of range?
+ if(ei<0 || ei>=num)
+ return false;
+
+ return elements[ei].equals(e);
+ }
+
+ return false;
+ }
+
+ public Iterator iterator()
+ {
+ return new iterator(this);
+ }
+
+ /**
+ * Inner class to implement an Iterator
+ */
+ protected class iterator implements Iterator
+ {
+ protected DCollection c;
+ protected int i;
+
+ public iterator(DCollection aCollection)
+ {
+ c=aCollection;
+ i=0;
+ }
+
+ public boolean hasNext()
+ {
+ return i<c.size();
+ }
+
+ public Object next() {
+ return c.getElement(i++);
+ }
+
+ public void remove() {
+ }
+ }
+
+ public synchronized Object[] toArray()
+ {
+ Object o[] = new Object[num];
+ System.arraycopy(elements,0,o,0,num);
+ return o;
+ }
+
+ public Object[] toArray(Object[] parm1)
+ {
+ /**@todo: Implement this java.util.Collection method*/
+ throw new java.lang.UnsupportedOperationException("Method toArray() not yet implemented.");
+ }
+
+ /**
+ * Adds a node to the Collection, and sets it's ID to its position in the Collection
+ */
+ public synchronized boolean add(Object parm1)
+ {
+ if(parm1 instanceof DElement) {
+ DElement e = (DElement) parm1;
+
+ // Do nothing if it's already in a Collection
+ if(e.getID()>-1) {
+ return false;
+ }
+
+ // Add to the Collection
+ resize();
+ e.setID(num);
+ elements[num++] = e;
+ return true;
+ }
+ return false;
+ }
+
+ public synchronized boolean remove(Object parm1)
+ {
+ if(parm1 instanceof DElement) {
+ DElement e = (DElement) parm1;
+ int ei = e.getID();
+ if(ei<0 || ei>=num)
+ return false;
+
+ // Mark the node as parentless
+ e.setID(-1);
+
+ // Now remove from the array by moving latter nodes, fixing their ids
+ // in the process
+ for(int j=ei,k=ei+1;k<num;j++,k++) {
+ elements[j]=elements[k];
+ elements[j].setID(j);
+ }
+ num--;
+ return true;
+ }
+
+ return false;
+ }
+
+ public boolean containsAll(Collection parm1)
+ {
+ /**@todo: Implement this java.util.Collection method*/
+ throw new java.lang.UnsupportedOperationException("Method containsAll() not yet implemented.");
+ }
+
+ public boolean addAll(Collection parm1)
+ {
+ /**@todo: Implement this java.util.Collection method*/
+ throw new java.lang.UnsupportedOperationException("Method addAll() not yet implemented.");
+ }
+
+ public boolean removeAll(Collection parm1)
+ {
+ /**@todo: Implement this java.util.Collection method*/
+ throw new java.lang.UnsupportedOperationException("Method removeAll() not yet implemented.");
+ }
+
+ public boolean retainAll(Collection parm1)
+ {
+ /**@todo: Implement this java.util.Collection method*/
+ throw new java.lang.UnsupportedOperationException("Method retainAll() not yet implemented.");
+ }
+
+ public synchronized void clear()
+ {
+ // Mark each node as parentless
+ for(int i=0;i<num;i++) {
+ elements[i].setID(-1);
+ }
+
+ // dispose the array
+ num=0;
+ max=0;
+ elements=null;
+ }
+
+ /**
+ * Returns the element with supplied id.
+ * @return element or null
+ */
+ public synchronized DElement getElement(int id)
+ {
+ if(id<0 || id>=num)
+ return null;
+
+ return elements[id];
+ }
+
+ /**
+ * Repairs the collection, ensuring all id's are correct
+ */
+ public synchronized void repair()
+ {
+ for(int i=0;i<num;i++) {
+ elements[i].setID(i);
+ }
+ }
+
+ public synchronized void saveXML(XMLFactory aFactory)
+ throws IOException, XMLFactoryException
+ {
+ for(int i=0;i<num;i++) {
+ elements[i].saveXML(aFactory);
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package uk.org.retep.dtu;
+
+public class DConstants
+{
+ /**
+ * A global version number
+ */
+ public static final String XML_VERSION_ID = "V7.1-2001-02-26";
+
+ /**
+ * XML Tag names
+ */
+ public static final String XML_DISPLAYNAME= "DISPLAYNAME";
+ public static final String XML_FROM = "FROM";
+ public static final String XML_ID = "ID";
+ public static final String XML_MODULE = "MODULE";
+ public static final String XML_NODE = "NODE";
+ public static final String XML_TO = "TO";
+ public static final String XML_TRANSFORM = "TRANSFORM";
+ public static final String XML_TYPE = "TYPE";
+ public static final String XML_VERSION = "VERSION";
+ public static final String XML_X = "X";
+ public static final String XML_Y = "Y";
+
+ public static final int NOP = 0; // No operation or always run transform
+ public static final int SUCCESS = 1; // Run transform only if DNode.OK
+ public static final int ERROR = 2; // Run transform only if DNode.ERROR
+
+ /**
+ * Node types 20-39 reserved for Transformation types
+ */
+ public static final int TRANSFORMBASE = 20;
+
+ /**
+ * Node types 20-99 reserved for Internal Node implementations
+ */
+ public static final int INTERNALBASE = 50;
+
+ /**
+ * Node types 100+ are for user extensions
+ */
+ public static final int USERBASE = 100;
+}
\ No newline at end of file
--- /dev/null
+package uk.org.retep.dtu;
+
+import uk.org.retep.xml.core.XMLFactory;
+import uk.org.retep.xml.core.XMLFactoryException;
+
+import java.io.IOException;
+
+public interface DElement
+{
+ /**
+ * Fetch the unique ID of this Element
+ */
+ public int getID();
+
+ /**
+ * Sets the unique id - normally set by DCollection
+ */
+ public void setID(int id);
+
+ /**
+ * @return the type of the Element
+ */
+ public int getType();
+
+ /**
+ * Set's the element type
+ */
+ public void setType(int aType);
+
+ public void saveXML(XMLFactory aFactory) throws IOException, XMLFactoryException;
+}
\ No newline at end of file
--- /dev/null
+package uk.org.retep.dtu;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+
+public class DEnvironment
+{
+ protected HashMap dsrc;
+
+ public DEnvironment()
+ {
+ dsrc=new HashMap();
+ }
+
+ public void addDataSource(String aKey,Object aObject)
+ {
+ dsrc.put(aKey,aObject);
+ }
+
+ public Object getDataSource(String aKey)
+ {
+ return dsrc.get(aKey);
+ }
+
+ public Iterator getDataSources()
+ {
+ return dsrc.values().iterator();
+ }
+}
\ No newline at end of file
--- /dev/null
+package uk.org.retep.dtu;
+
+import uk.org.retep.xml.core.XMLFactory;
+import uk.org.retep.xml.core.XMLFactoryException;
+import uk.org.retep.xml.parser.TagListener;
+import uk.org.retep.util.Logger;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * DModule represents a programatic module of steps used within the DTU
+ */
+public class DModule implements Serializable
+{
+ // The nodes and transitions between them
+ protected DCollection nodes;
+
+ protected String displayName;
+
+ public static final String DEFAULT_DISPLAYNAME = "unnamed module";
+
+ public DModule()
+ {
+ nodes=new DCollection();
+ displayName=DEFAULT_DISPLAYNAME;
+ Logger.log(Logger.DEBUG,"new DModule",this);
+ }
+
+ // Expensive!
+ public DNode getNode(int id)
+ {
+ return (DNode) nodes.getElement(id);
+ }
+
+ public DNode addNode(DNode aNode)
+ {
+ Logger.log(Logger.DEBUG,"DModule.addNode",aNode);
+ nodes.add(aNode);
+ return aNode;
+ }
+
+ public void removeNode(DNode aNode)
+ {
+ Logger.log(Logger.DEBUG,"DModule.removeNode",aNode);
+ nodes.remove(aNode);
+ }
+
+ public void clear()
+ {
+ Logger.log(Logger.DEBUG,"DModule.clear",this);
+ nodes.clear();
+ }
+
+ public void setDisplayName(String aName)
+ {
+ Logger.log(Logger.DEBUG,"DModule.setDisplayName",aName);
+ displayName = aName;
+ }
+
+ public String getDisplayName()
+ {
+ return displayName;
+ }
+
+ public Iterator iterator()
+ {
+ return nodes.iterator();
+ }
+
+ /**
+ * Writes an XML representation of this module to an XMLFactory. The caller
+ * must close the factory after use!
+ */
+ public synchronized void saveXML(XMLFactory aFactory)
+ throws IOException, XMLFactoryException
+ {
+ Logger.log(Logger.DEBUG,"DModule.saveXML start",this);
+ Iterator it;
+
+ aFactory.startTag(DConstants.XML_MODULE);
+ aFactory.addAttribute(DConstants.XML_DISPLAYNAME,displayName);
+ aFactory.addAttribute(DConstants.XML_VERSION,DConstants.XML_VERSION_ID);
+
+ // The nodes
+ nodes.saveXML(aFactory);
+
+ // The transforms
+ //trans.saveXML(aFactory);
+
+ aFactory.endTag(); // MODULE
+ Logger.log(Logger.DEBUG,"DModule.saveXML end",this);
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package uk.org.retep.dtu;
+
+import uk.org.retep.xml.core.XMLFactory;
+import uk.org.retep.xml.core.XMLFactoryException;
+import uk.org.retep.xml.parser.TagHandler;
+import uk.org.retep.xml.parser.TagListener;
+import uk.org.retep.util.Logger;
+
+import java.io.CharArrayWriter;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.ArrayList;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.Parser;
+import org.xml.sax.SAXException;
+import javax.xml.parsers.ParserConfigurationException;
+
+public class DModuleXML implements TagListener
+{
+ protected TagHandler handler;
+
+ protected DModule module = null;
+ protected DNode node = null;
+ protected DTransform trans = null;
+
+ protected ArrayList txmap;
+
+ public DModuleXML()
+ {
+ handler = new TagHandler();
+ handler.addTagListener(this);
+
+ txmap = new ArrayList();
+
+ Logger.log(Logger.DEBUG,"DModuleXML initialised");
+ }
+
+ public TagHandler getTagHandler()
+ {
+ return handler;
+ }
+
+ /**
+ * Used to optimise the switch handling in tagStart.
+ *
+ * The values of each T_* constant must match the corresponding element no
+ * in the tags static array.
+ */
+ private static final int T_DEFAULT=-1;
+ private static final int T_MODULE =0;
+ private static final int T_NODE =1;
+ private static final int T_TRANS =2;
+ private static final String tags[] = {
+ DConstants.XML_MODULE,
+ DConstants.XML_NODE,
+ DConstants.XML_TRANSFORM
+ };
+
+ /**
+ * This is called when a tag has just been started.
+ * <p><b>NB:</b> args is volatile, so if you use it beyond the lifetime of
+ * this call, then you must make a copy of the HashMap (and not use simply
+ * store this HashMap).
+ * @param level The number of tags above this
+ * @param tag The tag name
+ * @param args A HashMap of any arguments
+ */
+ public void tagStart(int level,String tag,HashMap args)
+ {
+ Logger.log(Logger.DEBUG,"DModuleXML.tagStart",tag);
+
+ // Prefetch some common attributes
+ String sType = (String) args.get(DConstants.XML_TYPE);
+ String sX = (String) args.get(DConstants.XML_X);
+ String sY = (String) args.get(DConstants.XML_Y);
+
+ int type=-1,x=-1,y=-1;
+
+ if(sType!=null) {
+ type = Integer.parseInt(sType);
+ }
+
+ if(sX!=null) {
+ y = Integer.parseInt(sX);
+ }
+
+ if(sY!=null) {
+ x = Integer.parseInt(sY);
+ }
+
+ // Match the tag against the tags array (used for switch() )
+ int tagID=T_DEFAULT;
+ for(int i=0;i<tags.length;i++) {
+ if(tag.equals(tags[i])) {
+ tagID=i;
+ }
+ }
+
+ switch(tagID)
+ {
+ // The main module tag
+ case T_MODULE:
+ module = new DModule();
+
+ String sDisplayName = (String) args.get(DConstants.XML_DISPLAYNAME);
+ if(sDisplayName!=null) {
+ module.setDisplayName(sDisplayName);
+ }
+ break;
+
+ // Basic nodes
+ case T_NODE:
+ node = new DNode();
+ node.setType(type);
+ module.addNode(node);
+ break;
+
+ // Basic transforms
+ case T_TRANS:
+ trans = new DTransform();
+ trans.setType(type);
+
+ // When finished we fix the transforms
+ int to = Integer.parseInt((String) args.get(DConstants.XML_TO));
+ txmap.add(new tx(node,trans,to));
+
+ break;
+
+ default:
+ // ignore unknown tags for now
+ break;
+ }
+ }
+
+ protected class tx
+ {
+ public DNode node;
+ public DTransform transform;
+ public int toID;
+
+ public tx(DNode aNode,DTransform aTransform,int aID)
+ {
+ node=aNode;
+ transform=aTransform;
+ toID=aID;
+ }
+ }
+
+ /**
+ * This method is called by ContHandler to process a tag once it has been
+ * fully processed.
+ * <p><b>NB:</b> content is volatile, so you must copy its contents if you use
+ * it beyond the lifetime of this call.
+ * @param content CharArrayWriter containing the content of the tag.
+ */
+ public void tagContent(CharArrayWriter content)
+ {
+ // Ignore
+ }
+
+ public void fixTransforms()
+ {
+ DNode to;
+ Iterator it = txmap.iterator();
+
+ while(it.hasNext()) {
+ tx x = (tx) it.next();
+
+ //Logger.log(Logger.DEBUG,"Fixing transform "+x.toID,x.transform,Integer.toString(x.node.getID()),Integer.toString(module.getNode(x.toID).getID()));
+ to = module.getNode(x.toID);
+
+ x.transform.setFrom(x.node);
+ x.transform.setTo(to);
+ //to.setFrom(x.transform);
+ }
+
+ }
+
+ /**
+ * Parse an InputSource and return the contained module.
+ * @return DModule loaded, null if the xml file does not contain a module.
+ */
+ public DModule parse(InputSource is)
+ throws IOException,SAXException
+ {
+ getTagHandler().parse(is);
+ fixTransforms();
+ return module;
+ }
+
+ /**
+ * Parse an uri and return the contained module.
+ * @return DModule loaded, null if the xml file does not contain a module.
+ */
+ public DModule parse(String uri)
+ throws IOException,SAXException
+ {
+ getTagHandler().parse(uri);
+ fixTransforms();
+ return module;
+ }
+
+ /**
+ * Debug test - read xml from one file and save to another.
+ */
+ public static void main(String args[]) throws Exception
+ {
+ if(args.length!=2) {
+ System.err.println("Syntax: java DModuleXML in-file out-file");
+ System.exit(1);
+ }
+
+ Logger.setLevel(Logger.DEBUG);
+
+ Logger.log(Logger.INFO,"DModuleXML Read test1.xml");
+ DModuleXML dm = new DModuleXML();
+ DModule module = dm.parse(new InputSource(new FileInputStream(args[0])));
+
+ Logger.log(Logger.INFO,"Parse complete");
+
+ Logger.log(Logger.INFO,"DModuleXML Write XML");
+ FileWriter fw = new FileWriter(args[1]);
+ module.saveXML(new XMLFactory(fw));
+ fw.close();
+ Logger.log(Logger.INFO,"Write complete");
+
+ DProcessor.run(module);
+ }
+}
\ No newline at end of file
--- /dev/null
+package uk.org.retep.dtu;
+
+import uk.org.retep.util.Logger;
+import uk.org.retep.xml.core.XMLFactory;
+import uk.org.retep.xml.core.XMLFactoryException;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Iterator;
+
+/**
+ * This is the base class for all nodes.
+ */
+public class DNode implements DElement, Serializable
+{
+ // The id of this node
+ protected int id;
+
+ // The type of this node
+ protected int type;
+
+ protected int x,y;
+
+ public static final int OK = 0; // Node last ran fine
+ public static final int ERROR = 1; // Node failed on last run
+
+ /**
+ * This type of node does nothing
+ */
+ public static int NOP = 0; // No action
+
+ public DNode()
+ {
+ this(NOP);
+ }
+
+ public DNode(int aType)
+ {
+ id=-1;
+ type=aType;
+
+ // Init the transform linkage
+ mf=mt=5;
+ nf=nt=0;
+ fn = new DTransform[mf];
+ tn = new DTransform[mt];
+
+ Logger.log(Logger.DEBUG,"new DNode");
+ }
+
+ public int getID()
+ {
+ return id;
+ }
+
+ public void setID(int aID)
+ {
+ id=aID;
+ Logger.log(Logger.DEBUG,"DNode.setID",aID);
+ }
+
+ public int getType()
+ {
+ return type;
+ }
+
+ public void setType(int aType)
+ {
+ type=aType;
+ Logger.log(Logger.DEBUG,"DNode.setType",aType);
+ }
+
+ /**
+ */
+ public void saveXML(XMLFactory aFactory)
+ throws IOException, XMLFactoryException
+ {
+ Logger.log(Logger.DEBUG,"DNode.saveXML start",this);
+ Iterator it;
+
+ aFactory.startTag(DConstants.XML_NODE);
+ aFactory.addAttribute(DConstants.XML_ID,new Integer(getID()));
+ aFactory.addAttribute(DConstants.XML_TYPE,new Integer(getType()));
+
+ // used for display only
+ aFactory.addAttribute(DConstants.XML_X,new Integer(getX()));
+ aFactory.addAttribute(DConstants.XML_Y,new Integer(getY()));
+
+ // Save the transforms here (only the from list required)
+ for(int i=0;i<nf;i++) {
+ fn[i].saveXML(aFactory);
+ }
+
+ aFactory.endTag(); // NODE
+ Logger.log(Logger.DEBUG,"DNode.saveXML finish",this);
+ }
+
+ public void setPosition(int aX,int aY)
+ {
+ x=aX;
+ y=aY;
+ }
+
+ public int getX()
+ {
+ return x;
+ }
+
+ public int getY()
+ {
+ return y;
+ }
+
+ public void setX(int aX)
+ {
+ x=aX;
+ }
+
+ public void setY(int aY)
+ {
+ y=aY;
+ }
+
+ /**
+ * This must be overidden to do something
+ * @return Return status
+ */
+ public int run(DEnvironment env)
+ {
+ return OK;
+ }
+
+ /**
+ * Node Transforms...
+ */
+ protected int nf,mf,nt,mt;
+ protected DTransform fn[],tn[];
+
+ /**
+ * Executes the transform
+ */
+ public DTransform getTransform(int aID)
+ {
+ return tn[aID];
+ }
+
+ /**
+ * @return number of transforms
+ */
+ public int getFromTransforms()
+ {
+ return nf;
+ }
+
+ /**
+ * @return number of transforms
+ */
+ public int getToTransforms()
+ {
+ return nt;
+ }
+
+ /**
+ * Adds a transform to this node (called by DTransform)
+ */
+ protected synchronized void setFrom(DTransform aTransform)
+ {
+ for(int i=0;i<nf;i++) {
+ if(fn[i].equals(aTransform)) {
+ return;
+ }
+ }
+ if(nf>=mf) {
+ mf+=5;
+ DTransform nn[] = new DTransform[mf];
+ System.arraycopy(fn,0,nn,0,nf);
+ fn=nn;
+ }
+ fn[nf++]=aTransform;
+ }
+
+ /**
+ * Adds a transform to this node (called by DTransform)
+ */
+ protected synchronized void setTo(DTransform aTransform)
+ {
+ for(int i=0;i<nt;i++) {
+ if(tn[i].equals(aTransform)) {
+ return;
+ }
+ }
+ if(nt>=mt) {
+ mt+=5;
+ DTransform nn[] = new DTransform[mt];
+ System.arraycopy(tn,0,nn,0,nt);
+ tn=nn;
+ }
+ tn[nt++]=aTransform;
+ }
+
+ /**
+ * Removes a transform (called by DTransform)
+ */
+ protected synchronized void removeFrom(DTransform aTransform)
+ {
+ for(int i=0;i<nf;i++) {
+ if(tn[i].equals(aTransform)) {
+ for(int j=i+1;j<nf;j++,i++) {
+ fn[i]=fn[j];
+ }
+ nf--;
+ return;
+ }
+ }
+ }
+
+ /**
+ * Removes a transform (called by DTransform)
+ */
+ protected synchronized void removeTo(DTransform aTransform)
+ {
+ for(int i=0;i<nt;i++) {
+ if(tn[i].equals(aTransform)) {
+ for(int j=i+1;j<nt;j++,i++) {
+ tn[i]=tn[j];
+ }
+ nt--;
+ return;
+ }
+ }
+ }
+
+}
--- /dev/null
+package uk.org.retep.dtu;
+
+import uk.org.retep.util.Logger;
+
+import java.util.Iterator;
+
+/**
+ * This class processes a Module. It's implemented as a Thread and there can
+ * be many threads running on a single module
+ */
+public class DProcessor
+{
+ /**
+ * This starts a module
+ */
+ public static DProcessor run(DModule aModule) {
+ // 3600000 is 1 hour in milliseconds
+ return run(aModule,3600000);
+ }
+
+ /**
+ * This starts a module
+ */
+ public static DProcessor run(DModule aModule,long timeout) {
+ return new DProcessor(aModule,timeout);
+ }
+
+ protected DProcessor(DModule aModule,long timeout) {
+ ThreadGroup group = new ThreadGroup(aModule.getDisplayName()+" DProcessor");
+
+ // Setup the environment
+ DEnvironment env = new DEnvironment();
+
+ // loop for any nodes without a transform pointing _to_ it.
+ Iterator it = aModule.iterator();
+ while(it.hasNext()) {
+ DNode node = (DNode) it.next();
+
+ // Only start if we have no predecessor
+ if(node.getFromTransforms()==0) {
+ proc proc = new proc(group,aModule,node,env);
+ proc.start();
+ }
+ }
+
+ // Now wait until all the threads have finished
+ boolean running=true;
+ try {
+ int cnt=1; // must loop at least once!
+
+ while(cnt>0) {
+ int numThreads = group.activeCount();
+ Thread threads[] = new Thread[numThreads];
+ cnt = group.enumerate(threads,false);
+
+ //Logger.log(Logger.DEBUG,"Waiting on threads",cnt);
+ while(cnt>0) {
+ //Logger.log(Logger.DEBUG,"Waiting on thread",cnt);
+ threads[--cnt].join(timeout);
+ }
+
+ Logger.log(Logger.DEBUG,"All threads appear to have died, retesting");
+ }
+ } catch(InterruptedException ie) {
+ Logger.log(Logger.ERROR,"DProcessor, exception caught while waiting for threads to die",ie);
+ }
+
+ // finally close any open datasources
+ Logger.log(Logger.DEBUG,"DProcessor cleanup");
+
+ Logger.log(Logger.DEBUG,"DProcessor finished");
+ }
+
+ class proc implements Runnable
+ {
+ protected DModule module; // The module being run
+ protected DNode pc; // current Program Counter
+
+ protected DEnvironment env; // Shared environment
+
+ // Used when launching new threads only
+ protected DTransform trans; // If not null, a transform to run first
+ protected int status;
+
+ protected Thread thread;
+
+ /**
+ * Start processing from DNode aNode. This is called by DProcessor at
+ * initialisation only.
+ */
+ protected proc(ThreadGroup aGroup,DModule aModule,DNode aNode,DEnvironment aEnv)
+ {
+ // aGroup will be null when forking...
+ if(aGroup==null) {
+ thread = new Thread(this);
+ } else {
+ thread = new Thread(aGroup,this);
+ }
+
+ module = aModule;
+ pc = aNode;
+ env = aEnv;
+ }
+
+ /**
+ * Start processing the DTransform aTransform from aNode (does not execute
+ * the node). This is called by this inner class itself when forking new
+ * threads.
+ */
+ protected proc(DModule aModule,DNode aNode,DEnvironment aEnv,DTransform aTransform,int aStatus)
+ {
+ this(null,aModule,aNode,aEnv);
+ trans = aTransform;
+ status = aStatus;
+ }
+
+ /**
+ * Start this thread
+ */
+ public void start()
+ {
+ thread.start();
+ }
+
+ public void run()
+ {
+ // Handle an initial transform first. It's used when a new Thread was created.
+ if(trans!=null) {
+ transform(trans,false,status);
+ trans=null;
+ }
+
+ while(pc!=null) {
+ //Logger.log(Logger.DEBUG,"running node ",pc.getID());
+
+ // Process the node
+ int status = pc.run(env);
+ //Logger.log(Logger.DEBUG," status ",status);
+
+ // Now the transforms. This thread continues with the first one that runs,
+ // but any others that will also run will do so in their own thread.
+ // If no transform runs (or there are none), then the thread will die.
+ int numTrans = pc.getToTransforms();
+ boolean fork=false;
+ for(int i=0;i<numTrans;i++) {
+ fork = transform(pc.getTransform(i),fork,status);
+ //Logger.log(Logger.DEBUG,"fork",fork?"true":"false");
+ }
+ //Logger.log(Logger.DEBUG,"fork",fork?"true":"false");
+ if(!fork) {
+ // No transforms ran, so we quit this thread
+ pc=null;
+ }
+
+ // This lets the other threads a chance to run
+ Thread.yield();
+ }
+ }
+
+ /**
+ * This executes a transform
+ * @param aTransform DTransform to execute
+ * @param fork true then a new process is triggered
+ * @param status The return status of the previous node
+ * @return true if the transform ran or a fork occured.
+ */
+ public boolean transform(DTransform aTransform,boolean fork,int status)
+ {
+ // Check to see if the transform will run (based on the calling nodes return
+ // status
+ if(!aTransform.willRun(status,env)) {
+ return false;
+ }
+
+ if(fork) {
+ // Create the new processor but this time we want a transform to run
+ proc proc = new proc(module,pc,env,aTransform,status);
+ return true;
+ }
+
+ if(aTransform.run(env)) {
+ pc=aTransform.getTo();
+ return true;
+ }
+
+ return false;
+ }
+
+ } // class proc
+
+}
\ No newline at end of file
--- /dev/null
+package uk.org.retep.dtu;
+
+import uk.org.retep.xml.core.XMLFactory;
+import uk.org.retep.xml.core.XMLFactoryException;
+
+import java.io.IOException;
+
+/**
+ * This manages the links between two nodes.
+ */
+public class DTransform
+{
+ protected DNode from,to;
+ protected int type;
+
+ public DTransform()
+ {
+ this(null,null);
+ }
+
+ public DTransform(DNode aFrom,DNode aTo)
+ {
+ from=aFrom;
+ to=aTo;
+ }
+
+ public int getType()
+ {
+ return type;
+ }
+
+ public void setType(int aType)
+ {
+ type=aType;
+ }
+
+ public void setFrom(DNode aNode)
+ {
+ if(from!=null) {
+ from.removeTo(this);
+ }
+
+ from=aNode;
+ from.setTo(this);
+ }
+
+ public DNode getFrom()
+ {
+ return from;
+ }
+
+ public void setTo(DNode aNode)
+ {
+ if(to!=null) {
+ to.removeFrom(this);
+ }
+
+ to=aNode;
+ aNode.setFrom(this);
+ }
+
+ public DNode getTo()
+ {
+ return to;
+ }
+
+ /**
+ * This ensures the minimum tag/attributes are generated.
+ * To extend, extend saveCustomXML() which is called by this method
+ * appropriately.
+ */
+ public final void saveXML(XMLFactory aFactory)
+ throws IOException, XMLFactoryException
+ {
+ // Bare minimum is the tag type, and the destination node's id
+ aFactory.startTag(DConstants.XML_TRANSFORM);
+ aFactory.addAttribute(DConstants.XML_TYPE,Integer.toString(getType()));
+ aFactory.addAttribute(DConstants.XML_TO,Integer.toString(to.getID()));
+ saveCustomXML(aFactory);
+ aFactory.endTag();
+ }
+
+ /**
+ * Custom transformations must overide this method and write direct to the
+ * supplied XMLFactory. A tag is currently open when the method is called, but
+ * is closed immediately this method exits.
+ */
+ public void saveCustomXML(XMLFactory aFactory)
+ throws IOException, XMLFactoryException
+ {
+ // Default method does nothing...
+ }
+
+ /**
+ * Checks to see if we should run based on the calling nodes status. Overide
+ * this to add this sort of checking.
+ *
+ * @param status The return status of the calling node
+ * @param env DEnvironment we are using
+ * @return true if we will run.
+ */
+ public boolean willRun(int status,DEnvironment env)
+ {
+ switch(getType())
+ {
+ // NOP is the generic link - always run
+ case DConstants.NOP:
+ return true;
+
+ // SUCCESS only runs if the previous node was OK
+ case DConstants.SUCCESS:
+ return status==DNode.OK;
+
+ case DConstants.ERROR:
+ return status==DNode.ERROR;
+
+ // Default - always run. Overide the method if you need to change this
+ default:
+ return true;
+ }
+ }
+
+ /**
+ * Overide this for a transform to do something.
+ * @param env DEnvironment we are using
+ * @return true if we actually ran. DProcessor will jump to the getTo() node if so.
+ */
+ public boolean run(DEnvironment env)
+ {
+ return true;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+#Written by Retep PropertyEditor\r
+#Sat Mar 03 16:29:44 GMT+00:00 2001\r
+tool.hba=pg_hba.conf editor\r
+tool.hba.class=uk.org.retep.util.hba.Editor\r
+tool.proped.class=uk.org.retep.util.proped.PropertyEditor\r
+tool.hba.type=Misc\r
+tool.proped.type=Misc\r
+tool.proped=Properties Editor\r
--- /dev/null
+package uk.org.retep.tools;
+
+import javax.swing.JMenuBar;
+
+/**
+ * Tools can implement this interface to provide the parent manager (the big
+ * application or the StandaloneApp class) enough details to display them.
+ *
+ * If a tool does not implement this class, it gets basic treatment.
+ *
+ * @author
+ * @version 1.0
+ */
+
+public interface Tool
+{
+ /**
+ * @return the JMenuBar for this tool, null if none.
+ */
+ public JMenuBar getMenuBar();
+
+ /**
+ * @return the title string to go into the JFrame/JInternalFrame's title bar.
+ */
+ public String getTitle();
+
+ /**
+ * Called by StandaloneApp to indicate this is within a StandaloneApp.
+ * You should assume you are not in standalone mode until this is called.
+ */
+ public void setStandaloneMode(boolean aMode);
+
+}
\ No newline at end of file
--- /dev/null
+package uk.org.retep.util;
+
+import java.awt.*;
+import javax.swing.*;
+import java.awt.event.*;
+
+/**
+ * Display an Exception to the user
+ * @author
+ * @version 1.0
+ */
+
+public class ExceptionDialog extends JDialog
+{
+ // This is used to store the parent frame.
+ // Classes like StandaloneApp set's this so that the
+ // displayException() method can work without knowing/finding out
+ // the parent Frame/JFrame.
+ private static Frame globalFrame;
+
+ private static ExceptionDialog globalDialog;
+
+ JPanel panel1 = new JPanel();
+ BorderLayout borderLayout1 = new BorderLayout();
+ JTextArea message = new JTextArea();
+ JPanel jPanel1 = new JPanel();
+ JButton jButton1 = new JButton();
+ GridLayout gridLayout1 = new GridLayout();
+ JButton jButton2 = new JButton();
+ JLabel jLabel1 = new JLabel();
+
+ public ExceptionDialog(Frame frame, String title, boolean modal)
+ {
+ super(frame, title, modal);
+ try
+ {
+ jbInit();
+ pack();
+ }
+ catch(Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+
+ public ExceptionDialog()
+ {
+ this(null, "", false);
+ }
+ void jbInit() throws Exception
+ {
+ panel1.setLayout(borderLayout1);
+ message.setBorder(BorderFactory.createLoweredBevelBorder());
+ message.setText("jTextArea1");
+ message.setBackground(Color.lightGray);
+ message.setEditable(false);
+ jButton1.setText("Close");
+ jButton1.addActionListener(new java.awt.event.ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ jButton1_actionPerformed(e);
+ }
+ });
+ jPanel1.setLayout(gridLayout1);
+ jButton2.setEnabled(false);
+ jButton2.setText("Stack Trace");
+ jLabel1.setEnabled(false);
+ getContentPane().add(panel1);
+ panel1.add(message, BorderLayout.CENTER);
+ this.getContentPane().add(jPanel1, BorderLayout.SOUTH);
+ jPanel1.add(jButton2, null);
+ jPanel1.add(jLabel1, null);
+ jPanel1.add(jButton1, null);
+ }
+
+ /**
+ * Sets the Frame used to display all dialog boxes.
+ */
+ public static void setFrame(Frame aFrame)
+ {
+ globalFrame = aFrame;
+ }
+
+ /**
+ * Displays a dialog based on the exception
+ * @param ex Exception that was thrown
+ */
+ public static void displayException(Exception ex)
+ {
+ displayException(ex,null);
+ }
+
+ /**
+ * Displays a dialog based on the exception
+ * @param ex Exception that was thrown
+ */
+ public static void displayException(Exception ex,String msg)
+ {
+ String cname = ex.getClass().getName();
+ int i=cname.lastIndexOf(".");
+ displayException(cname.substring(i+1),ex,msg);
+ }
+
+ public static void displayException(String title,Exception ex)
+ {
+ displayException(title,ex,null);
+ }
+
+ public static void displayException(String title,Exception ex,String msg)
+ {
+ Logger.log(Logger.ERROR,title,ex.getMessage());
+
+ // Default to a stack trace if no frame set
+ if(globalFrame==null) {
+ ex.printStackTrace();
+ return;
+ }
+
+ if(globalDialog==null) {
+ globalDialog=new ExceptionDialog(globalFrame,title,true);
+ globalDialog.pack();
+ }
+
+ globalDialog.setTitle(title);
+
+ if(msg!=null) {
+ globalDialog.message.setText(msg);
+ globalDialog.message.append(":\n");
+ }
+ globalDialog.message.append(ex.getMessage());
+
+ globalDialog.pack();
+ globalDialog.setVisible(true);
+ }
+
+ void jButton1_actionPerformed(ActionEvent e)
+ {
+ setVisible(false);
+ }
+}
\ No newline at end of file
--- /dev/null
+package uk.org.retep.util;
+
+import uk.org.retep.util.Logger;
+
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Properties;
+
+/**
+ * This is a Singleton that stores global properties, command line arguments
+ * etc.
+ *
+ * All tools are guranteed that this will exist.
+ *
+ * @author
+ * @version 1.0
+ */
+
+public class Globals
+{
+ private static final Globals SINGLETON = new Globals();
+
+ private Hashtable global= new Hashtable();
+ private Properties props = new Properties();
+ private ArrayList args = new ArrayList();
+
+ private Globals()
+ {
+ }
+
+ public static Globals getInstance()
+ {
+ return SINGLETON;
+ }
+
+ /**
+ * Retrieves an object from the global pool
+ * @param aKey key of the object
+ * @return The object, null if not found
+ */
+ public Object get(Object aKey)
+ {
+ return global.get(aKey);
+ }
+
+ /**
+ * Stores an object into the global pool
+ * @param aKey key of the object
+ * @param aObj the object to store
+ * @return aObj
+ */
+ public Object put(Object aKey,Object aObj)
+ {
+ return global.put(aKey,aObj);
+ }
+
+ /**
+ * Returns a Properties object of all properties
+ */
+ /*
+ public Properties getProperties()
+ {
+ return props;
+ }
+ */
+
+ /**
+ * @param aProp a property supplied to the command line
+ * @return property or NULL if not present
+ */
+ public String getProperty(String aProp)
+ {
+ return props.getProperty(aProp);
+ }
+
+ /**
+ * @param aProp a property supplied to the command line
+ * @param aDefault default to return if property was not supplied
+ * @return property value
+ */
+ public String getProperty(String aProp,String aDefault)
+ {
+ return props.getProperty(aProp,aDefault);
+ }
+
+ /**
+ * @param aID ID of the argument, 0 ... getArgumentCount()-1
+ * @return argument
+ */
+ public String getArgument(int aID)
+ {
+ return (String) args.get(aID);
+ }
+
+ /**
+ * Returns an array of String objects representing the arguments
+ */
+ public String[] getArguments()
+ {
+ return (String[]) args.toArray();
+ }
+
+ /**
+ * Returns an Iterator of the arguments
+ */
+ public Iterator getArgumentIterator()
+ {
+ return args.iterator();
+ }
+
+ /**
+ * @return number of arguments
+ */
+ public int getArgumentCount()
+ {
+ return args.size();
+ }
+
+ /**
+ * Parses the command line arguments
+ */
+ public void parseArguments(String[] aArgs)
+ throws Exception
+ {
+ for(int i=0;i<aArgs.length;i++) {
+ String arg = aArgs[i];
+ if(arg.startsWith("--") || arg.startsWith("-")) {
+ if(arg.length()>1) {
+ // Split the option at the first '=' char if any
+ int s = arg.startsWith("--") ? 2 : 1 ; // -- or -
+ int e = arg.indexOf("=");
+ String key,val;
+ if(e>s) {
+ // Format: -key=value
+ key=arg.substring(s,e-1);
+ val=arg.substring(e+1);
+ } else if(e>-1 && e<=s) {
+ // Can't have a property without a key!
+ throw new Exception("Invalid option -=");
+ } else {
+ key=arg.substring(s);
+ val=""; // can't be null
+ }
+
+ if(key.equals("d")) {
+ // -d | --d is reserved to set the Logger level
+ int level=0;
+ if(!val.equals("")) {
+ level=Integer.parseInt(val);
+ }
+ Logger.setLevel(level);
+ } else {
+ // Add all other properties into the Properties object
+ props.put(key,val);
+ Logger.log(Logger.INFO,"Argument",key,val);
+ }
+
+ } else {
+ // Just a - on its own?
+ System.out.println("Unknown option: -");
+ }
+ } else {
+ // Add the argument to the array
+ args.add(arg);
+ }
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package uk.org.retep.util;
+
+import java.io.CharArrayWriter;
+import java.io.PrintWriter;
+
+public class Logger
+{
+ protected static int level;
+ protected static PrintWriter logger;
+
+ public static final int NONE = -1;
+ public static final int INFO = 0;
+ public static final int ERROR = 1;
+ public static final int DEBUG = 2;
+ public static final int ALL = 3;
+
+ static {
+ level = NONE;
+ logger = null;
+ };
+
+ private static final String levels[] = {
+ "INFO :",
+ "ERROR:",
+ "DEBUG:",
+ "ALL :"
+ };
+
+ public static void setLevel(int aLevel)
+ {
+ // Incase we have not yet set a logger
+ if(logger==null) {
+ logger = new PrintWriter(System.out);
+ }
+
+ if(aLevel<NONE) {
+ aLevel=NONE;
+ } else if(aLevel>ALL) {
+ aLevel=ALL;
+ }
+
+ level=aLevel;
+
+ if(level>NONE) {
+ log(INFO,"Log level changed to",level,levels[level]);
+ }
+ }
+
+ public static void setLogger(PrintWriter pw)
+ {
+ if(logger!=null) {
+ try {
+ logger.flush();
+ logger.close();
+ } catch(Exception ex) {
+ logger=pw;
+ log(ERROR,"Exception while closing logger",ex);
+ }
+ }
+ logger=pw;
+ }
+
+ public static void log(String msg)
+ {
+ log(INFO,msg);
+ }
+
+ public static void log(int aLevel,String msg)
+ {
+ write(aLevel,msg,null);
+ }
+
+ public static void log(int aLevel,String msg,int arg1)
+ {
+ Object o[] = {new Integer(arg1)};
+ write(aLevel,msg,o);
+ }
+
+ public static void log(int aLevel,String msg,int arg1,Object arg2)
+ {
+ Object o[] = {new Integer(arg1),arg2};
+ write(aLevel,msg,o);
+ }
+
+ public static void log(int aLevel,String msg,double arg1)
+ {
+ Object o[] = {new Double(arg1)};
+ write(aLevel,msg,o);
+ }
+
+ public static void log(int aLevel,String msg,double arg1,Object arg2)
+ {
+ Object o[] = {new Double(arg1),arg2};
+ write(aLevel,msg,o);
+ }
+
+ public static void log(int aLevel,String msg,Object arg1)
+ {
+ Object o[] = {arg1};
+ write(aLevel,msg,o);
+ }
+
+ public static void log(int aLevel,String msg,Object arg1,Object arg2)
+ {
+ Object o[] = {arg1,arg2};
+ write(aLevel,msg,o);
+ }
+
+ public static void log(int aLevel,String msg,Object arg1,Object arg2,Object arg3)
+ {
+ Object o[] = {arg1,arg2,arg3};
+ write(aLevel,msg,o);
+ }
+
+ public static void log(int aLevel,String msg,Throwable t)
+ {
+ CharArrayWriter buffer = new CharArrayWriter();
+ PrintWriter printWriter = new PrintWriter(buffer);
+ t.printStackTrace(printWriter);
+ Object o[] = {buffer.toString()};
+ buffer.close();
+ write(aLevel,msg,o);
+ }
+
+ private static void write(int aLevel,String aMsg,Object args[])
+ {
+ // Can't be above ALL
+ if(aLevel>ALL) {
+ aLevel=ALL;
+ }
+
+ // Ignore if below or equal to NONE
+ if(aLevel<INFO || aLevel>level) {
+ return;
+ }
+
+ logger.print("Logger:");
+ logger.print(levels[aLevel]);
+ logger.print(aMsg);
+ if(args!=null) {
+ for(int a=0;a<args.length;a++) {
+ logger.print(":");
+ logger.print(args[a]);
+ }
+ }
+ logger.println();
+ logger.flush();
+ }
+
+}
--- /dev/null
+package uk.org.retep.util;
+
+import uk.org.retep.util.StandaloneApp;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+
+/**
+ * This is a template for your own Tools. Copy not extend this class. Please
+ * refer to Implementation for details.
+ *
+ * All you need to to is implement the init() method.
+ *
+ * $Id: Main.java,v 1.1 2001/03/05 09:15:36 peter Exp $
+ */
+
+public class Main extends StandaloneApp
+{
+ public Main(String[] args)
+ throws Exception
+ {
+ super(args);
+ }
+
+ public JComponent init()
+ throws Exception
+ {
+ // Create your tool here, then do things like load files based on the
+ // command line arguments. Then return that tool.
+
+ // NB: This just allows us to compile. You're implementation must return
+ // the Tool itself.
+ return new JLabel("Replace with your own tool!");
+ }
+
+ public static void main(String[] args)
+ throws Exception
+ {
+ Main main = new Main(args);
+ main.pack();
+ main.setVisible(true);
+ }
+}
--- /dev/null
+package uk.org.retep.util;
+
+import uk.org.retep.tools.Tool;
+import uk.org.retep.util.Globals;
+import uk.org.retep.util.ExceptionDialog;
+
+import java.awt.*;
+import javax.swing.*;
+import java.awt.event.*;
+
+/**
+ * This provides the basic services needed for enabling some of the tools to
+ * run in a Stand-alone fassion.
+ *
+ * Note: Because it's designed for standalone use, if this window is closed,
+ * the JVM is terminated. Do not use for normal application use.
+ *
+ * $Id: StandaloneApp.java,v 1.1 2001/03/05 09:15:36 peter Exp $
+ *
+ * @author
+ * @version 1.0
+ */
+
+public abstract class StandaloneApp extends JFrame
+{
+ public StandaloneApp(String[] aArgs)
+ throws Exception
+ {
+ super(); // Initialise JFrame
+
+ // Allow dialogs to work with us
+ ExceptionDialog.setFrame(this);
+
+ // Add a window listener
+ this.addWindowListener(new java.awt.event.WindowAdapter()
+ {
+ public void windowClosing(WindowEvent e)
+ {
+ System.exit(0);
+ }
+ });
+
+ // Parse the command line arguments
+ Globals.getInstance().parseArguments(aArgs);
+
+ // Now initialise this tool (init is overidden)
+ JComponent tool = init();
+
+ // Now add to this frame
+ this.getContentPane().add(tool, BorderLayout.CENTER);
+
+ // Finally call the Tool interface
+ if(tool instanceof Tool) {
+ Tool t = (Tool) tool;
+
+ // Notify the tool we are a standalone
+ t.setStandaloneMode(true);
+
+ // Fetch the title
+ setTitle(t.getTitle());
+
+ // and a MenuBar (if needed)
+ JMenuBar mb = t.getMenuBar();
+ if(mb!=null) {
+ setJMenuBar(t.getMenuBar());
+ }
+ } else {
+ // Ok, set a default title string
+ setTitle("RetepTools Standalone");
+ }
+
+ }
+
+ /**
+ * You must overide this method with your initialiser.
+ */
+ public abstract JComponent init() throws Exception;
+
+}
\ No newline at end of file
--- /dev/null
+package uk.org.retep.util.hba;
+
+import uk.org.retep.tools.Tool;
+import uk.org.retep.util.models.HBATableModel;
+
+import java.awt.*;
+import java.io.*;
+import java.util.*;
+import javax.swing.table.*;
+import javax.swing.*;
+
+/**
+ * pg_hba.conf editor (& repairer)
+ *
+ * @author
+ * @version 1.0
+ */
+
+public class Editor extends JPanel implements Tool
+{
+ BorderLayout borderLayout1 = new BorderLayout();
+ HBATableModel model = new HBATableModel();
+ JPanel jPanel1 = new JPanel();
+ GridBagLayout gridBagLayout1 = new GridBagLayout();
+ JLabel jLabel1 = new JLabel();
+ JComboBox typeEntry = new JComboBox();
+ JLabel jLabel2 = new JLabel();
+ JLabel jLabel3 = new JLabel();
+ JLabel jLabel4 = new JLabel();
+ JTextField ipEntry = new JTextField();
+ JTextField maskEntry = new JTextField();
+ JComboBox authEntry = new JComboBox();
+ JTextField argEntry = new JTextField();
+ JLabel jLabel5 = new JLabel();
+ JPanel jPanel2 = new JPanel();
+ FlowLayout flowLayout1 = new FlowLayout();
+ JButton jButton1 = new JButton();
+ JButton jButton2 = new JButton();
+ JScrollPane jScrollPane1 = new JScrollPane();
+ JButton jButton3 = new JButton();
+ JTable jTable1 = new JTable();
+
+ public Editor()
+ {
+ try
+ {
+ jbInit();
+ }
+ catch(Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+ void jbInit() throws Exception
+ {
+ this.setLayout(borderLayout1);
+ jTable1.setPreferredSize(new Dimension(600, 300));
+ jTable1.setModel(model);
+ this.setPreferredSize(new Dimension(600, 300));
+ this.add(jScrollPane1, BorderLayout.CENTER);
+ jScrollPane1.getViewport().add(jTable1, null);
+ jPanel1.setLayout(gridBagLayout1);
+ jLabel1.setText("Type");
+ jLabel2.setText("IP Address");
+ jLabel3.setText("Mask");
+ jLabel4.setText("Authentication");
+ ipEntry.setText("jTextField1");
+ maskEntry.setText("jTextField2");
+ argEntry.setText("jTextField3");
+ jLabel5.setText("Argument");
+ jPanel2.setLayout(flowLayout1);
+ jButton1.setText("New entry");
+ jButton2.setText("Validate");
+ jButton3.setText("Devele");
+ this.add(jPanel1, BorderLayout.SOUTH);
+ jPanel1.add(jLabel1, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0
+ ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jPanel1.add(typeEntry, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0
+ ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jPanel1.add(jLabel2, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0
+ ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jPanel1.add(jLabel3, new GridBagConstraints(3, 1, 1, 1, 0.0, 0.0
+ ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jPanel1.add(jLabel4, new GridBagConstraints(1, 2, 1, 1, 0.0, 0.0
+ ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jPanel1.add(ipEntry, new GridBagConstraints(2, 1, 1, 1, 0.0, 0.0
+ ,GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
+ jPanel1.add(maskEntry, new GridBagConstraints(4, 1, 1, 1, 0.0, 0.0
+ ,GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
+ jPanel1.add(authEntry, new GridBagConstraints(2, 2, 1, 1, 0.0, 0.0
+ ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jPanel1.add(argEntry, new GridBagConstraints(4, 2, 1, 1, 0.0, 0.0
+ ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jPanel1.add(jLabel5, new GridBagConstraints(3, 2, 1, 1, 0.0, 0.0
+ ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+ jPanel1.add(jPanel2, new GridBagConstraints(0, 3, 5, 1, 0.0, 0.0
+ ,GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
+ jPanel2.add(jButton3, null);
+ jPanel2.add(jButton1, null);
+ jPanel2.add(jButton2, null);
+ }
+
+ public void openFile(String aFilename)
+ throws IOException
+ {
+ FileInputStream fis = new FileInputStream(aFilename);
+ BufferedReader br = new BufferedReader(new InputStreamReader(fis));
+ ArrayList list = model.getArray();
+
+ String s = br.readLine();
+ while(s!=null) {
+ if(s.startsWith("#")) {
+ // ignore comments
+ } else {
+ Record rec = Record.parseLine(s);
+ if(rec!=null) {
+ rec.validate();
+ list.add(rec);
+ }
+ }
+ s=br.readLine();
+ }
+
+ model.fireTableDataChanged();
+ }
+
+ public JMenuBar getMenuBar()
+ {
+ return null;
+ }
+
+ public String getTitle()
+ {
+ return "pg_hba.conf Editor/Repair tool";
+ }
+
+ public void setStandaloneMode(boolean aMode)
+ {
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package uk.org.retep.util.hba;
+
+import uk.org.retep.util.ExceptionDialog;
+import uk.org.retep.util.Globals;
+import uk.org.retep.util.Logger;
+import uk.org.retep.util.StandaloneApp;
+
+import java.io.IOException;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+
+/**
+ * Standalone entry point for the Properties editor
+ *
+ * $Id: Main.java,v 1.1 2001/03/05 09:15:37 peter Exp $
+ */
+
+public class Main extends StandaloneApp
+{
+ public Main(String[] args)
+ throws Exception
+ {
+ super(args);
+ }
+
+ public JComponent init()
+ throws Exception
+ {
+ Globals globals = Globals.getInstance();
+
+ Editor editor = new Editor();
+
+ if(globals.getArgumentCount()>0) {
+ editor.openFile(globals.getArgument(0));
+ }
+
+ return editor;
+ }
+
+ public static void main(String[] args)
+ throws Exception
+ {
+ Main main = new Main(args);
+ main.pack();
+ main.setVisible(true);
+ }
+}
--- /dev/null
+package uk.org.retep.util.hba;
+
+import uk.org.retep.util.Logger;
+import uk.org.retep.util.misc.IPAddress;
+import uk.org.retep.util.misc.WStringTokenizer;
+
+/**
+ * Used to store the entries of a pg_hba.conf file
+ * @author
+ * @version 1.0
+ */
+
+public class Record
+{
+ int type;
+ String dbname;
+ IPAddress ip;
+ IPAddress mask;
+ int authType;
+ String authArg;
+
+ public static final int TYPE_LOCAL = 0;
+ public static final int TYPE_HOST = 1;
+ public static final int TYPE_HOSTSSL = 2;
+
+ public static final String types[] = {
+ "local","host","hostssl"
+ };
+
+ public static final int AUTH_TRUST = 0;
+ public static final int AUTH_PASSWORD = 1;
+ public static final int AUTH_CRYPT = 2;
+ public static final int AUTH_IDENT = 3;
+ public static final int AUTH_KRB4 = 4;
+ public static final int AUTH_KRB5 = 5;
+ public static final int AUTH_REJECT = 6;
+
+ public static final String auths[] = {
+ "trust","password","crypt",
+ "ident",
+ "krb4","krb5",
+ "reject"
+ };
+
+ private static final String spc = " ";
+
+ public Record()
+ {
+ }
+
+ public int getType()
+ {
+ return type;
+ }
+
+ public void setType(int aType)
+ {
+ type=aType;
+ }
+
+ public String getDatabase()
+ {
+ return dbname;
+ }
+
+ public void setDatabase(String aDB)
+ {
+ dbname=aDB;
+ }
+
+ public int getAuthType()
+ {
+ return authType;
+ }
+
+ public void setAuthType(int aType)
+ {
+ authType=aType;
+ }
+
+ public String getAuthArgs()
+ {
+ return authArg;
+ }
+
+ public void setAuthArgs(String aArg)
+ {
+ authArg=aArg;
+ }
+
+ public IPAddress getIP()
+ {
+ return ip;
+ }
+
+ public void setIP(String aArg)
+ {
+ setIP(new IPAddress(aArg));
+ }
+
+ public void setIP(IPAddress aArg)
+ {
+ ip=aArg;
+ }
+
+ public IPAddress getMask()
+ {
+ return mask;
+ }
+
+ public void setMask(String aArg)
+ {
+ setMask(new IPAddress(aArg));
+ }
+
+ public void setMask(IPAddress aArg)
+ {
+ mask=aArg;
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ write(buf);
+ return buf.toString();
+ }
+
+ public void write(StringBuffer buf)
+ {
+ buf.append(types[type]).append(spc);
+
+ if(type==TYPE_HOST || type==TYPE_HOSTSSL) {
+ buf.append(getIP()).append(spc);
+ buf.append(getMask()).append(spc);
+ }
+
+ buf.append(auths[authType]);
+
+ // Now the authArg
+ switch(type)
+ {
+ // These have no authArgs
+ case AUTH_TRUST:
+ case AUTH_REJECT:
+ case AUTH_KRB4:
+ case AUTH_KRB5:
+ break;
+
+ // These must have an arg
+ case AUTH_IDENT:
+ buf.append(spc).append(getAuthArgs());
+ break;
+
+ // These may have an optional arg
+ case AUTH_PASSWORD:
+ case AUTH_CRYPT:
+ if(!(authArg==null || authArg.equals("")))
+ buf.append(spc).append(getAuthArgs());
+ break;
+ }
+ }
+
+ private static WStringTokenizer tok;
+
+ public static Record parseLine(String s)
+ {
+ Record res = new Record();
+ int type;
+
+ if(s==null || s.equals("") || s.startsWith("#"))
+ return null;
+
+ if(tok==null)
+ tok=new WStringTokenizer();
+
+ tok.setString(s);
+
+ type=WStringTokenizer.matchToken(types,tok.nextToken());
+ res.setType(type);
+
+ res.setDatabase(tok.nextToken());
+
+ if(type==TYPE_HOST || type==TYPE_HOSTSSL) {
+ res.setIP(new IPAddress(tok.nextToken()));
+ res.setMask(new IPAddress(tok.nextToken()));
+ }
+
+ res.setAuthType(WStringTokenizer.matchToken(auths,tok.nextToken()));
+ res.setAuthArgs(tok.nextToken());
+
+ return res;
+ }
+
+ public static final int VALID = 0;
+ public static final int INVALID_TYPE = 1;
+ public static final int INVALID_IPREQUIRED = 2;
+
+ /**
+ * Validates the record
+ */
+ public int validate()
+ {
+ switch(type)
+ {
+ case TYPE_HOST:
+ case TYPE_HOSTSSL:
+ if(ip==null || ip.isInvalid()) {
+ Logger.log(Logger.INFO,"pg_hba.conf: IP missing or invalid - repairing");
+ setMask("127.0.0.1");
+ }
+
+ if(mask==null || mask.isInvalid() || !ip.validateMask(mask)) {
+ Logger.log(Logger.INFO,"pg_hba.conf: IP address without mask - repairing");
+ setMask(ip.getMask());
+ }
+
+ break;
+
+ case TYPE_LOCAL:
+ break;
+
+ default:
+ return INVALID_TYPE;
+ }
+
+ return VALID;
+ }
+
+ /*
+# host all 192.168.54.1 255.255.255.255 reject
+# host all 0.0.0.0 0.0.0.0 krb5
+# host all 192.168.0.0 255.255.0.0 ident omicron
+#
+
+local all trust
+host all 127.0.0.1 255.255.255.255 trust
+*/
+}
\ No newline at end of file
--- /dev/null
+package uk.org.retep.util.misc;
+
+import java.util.StringTokenizer;
+
+/**
+ * Represent an IP address
+ * @author
+ * @version 1.0
+ */
+
+public class IPAddress
+{
+ protected long address;
+ protected long b[] = new long[4];
+ protected boolean invalid=true;
+
+ public IPAddress()
+ {
+ }
+
+ public IPAddress(String s)
+ {
+ setAddress(s);
+ }
+
+ public synchronized void setAddress(String s)
+ {
+ if(s==null || s.equals("")) {
+ invalid=true;
+ return;
+ }
+
+ address=0;
+ StringTokenizer tok = new StringTokenizer(s,".");
+ int i=0;
+ while(i<4 && tok.hasMoreElements()) {
+ b[i++] = Long.parseLong(tok.nextToken());
+ }
+ while(i<4) {
+ b[i++]=0;
+ }
+
+ invalid=false;
+ refresh();
+ }
+
+ public void refresh()
+ {
+ if(invalid)
+ return;
+ address = (b[0]<<24) | (b[1]<<16) | (b[2]<<8) | (b[3]);
+ }
+
+ public boolean isInvalid()
+ {
+ refresh();
+ return invalid;
+ }
+
+ public String toString()
+ {
+ refresh();
+ if(invalid)
+ return "*INVALID*";
+
+ return Long.toString(b[0])+"."+Long.toString(b[1])+"."+Long.toString(b[2])+"."+Long.toString(b[3]);
+ }
+
+ public boolean equals(Object o)
+ {
+ if(o instanceof IPAddress) {
+ IPAddress ip = (IPAddress) o;
+
+ refresh();
+ ip.refresh();
+
+ if(ip.invalid == invalid)
+ return false;
+
+ return address==ip.address;
+ }
+ return false;
+ }
+
+ private static int gethoststart(long b)
+ {
+ if((b & 0x80)==0x00) return 1; // class A
+ if((b & 0xc0)==0x80) return 2; // class B
+ if((b & 0xe0)==0xc0) return 3; // class C
+ return 4; // class D
+ }
+
+ public boolean validateMask(IPAddress mask)
+ {
+ // If were a network check the host mask
+ int i=gethoststart(b[0]);
+System.out.println("Host start "+i);
+ while(i<4 && b[i]==0) {
+ if(mask.b[i++]>0)
+ return false;
+ }
+
+ for(i=0;i<4;i++) {
+ if((b[i]&mask.b[i])!=b[i])
+ return false;
+ }
+
+ return true;
+ }
+
+ public IPAddress getMask()
+ {
+ IPAddress mask = new IPAddress();
+ int i=3;
+ while(i>-1 && b[i]==0) {
+ mask.b[i--]=0;
+ }
+ while(i>-1) {
+ mask.b[i--]=255;
+ }
+ mask.invalid=false;
+ mask.refresh();
+ return mask;
+ }
+}
\ No newline at end of file
--- /dev/null
+package uk.org.retep.util.misc;
+
+import java.io.*;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.TreeMap;
+
+/**
+ * Misc Properties utilities..
+ * @author
+ * @version 1.0
+ */
+
+public class PropertiesIO
+{
+
+ public PropertiesIO()
+ {
+ }
+
+ /**
+ * Builds a TreeMap based on the given Properties object. This is useful
+ * because the keys will be in sorted order.
+ */
+ public static TreeMap getTreeMap(Properties p)
+ {
+ TreeMap map = new TreeMap();
+ Iterator e = p.keySet().iterator();
+ while(e.hasNext()) {
+ Object k = e.next();
+ map.put(k,p.get(k));
+ }
+ return map;
+ }
+
+ /**
+ * Writes a Properties file to the writer. This is similar to Properties.save
+ * except you can pick the key/value separator
+ */
+ public static synchronized void save(Properties p,OutputStream out,char sep,String header)
+ throws IOException
+ {
+ save(p,p.keySet().iterator(),out,sep,header);
+ }
+
+ /**
+ * Writes a Properties file to the writer. This is similar to Properties.save
+ * except you can pick the key/value separator and the keys are written
+ * in a sorted manner
+ */
+ public static synchronized void saveSorted(Properties p,OutputStream out,char sep,String header)
+ throws IOException
+ {
+ save(p,getTreeMap(p).keySet().iterator(),out,sep,header);
+ }
+
+ /**
+ * This is the same as save, only the keys in the enumeration are written.
+ */
+ public static synchronized void save(Properties p,Iterator e, OutputStream out,char sep,String header)
+ throws IOException
+ {
+ BufferedWriter w = new BufferedWriter(new OutputStreamWriter(out, "8859_1"));
+
+ if (header != null) {
+ w.write('#');
+ w.write(header);
+ w.newLine();
+ }
+
+ w.write('#');
+ w.write(new Date().toString());
+ w.newLine();
+
+ while(e.hasNext()) {
+ String key = (String)e.next();
+ w.write(encode(key,true));
+ w.write(sep);
+ w.write(encode((String)p.get(key),false));
+ w.newLine();
+ }
+ w.flush();
+ }
+
+ private static final String specialSaveChars = "=: \t\r\n\f#!";
+
+ /**
+ * Encodes a string in a way similar to the JDK's Properties method
+ */
+ public static String encode(String s, boolean escapeSpace)
+ {
+ int l=s.length();
+ StringBuffer buf = new StringBuffer(l<<1);
+
+ for(int i=0;i<l;i++) {
+ char c = s.charAt(i);
+ switch(c)
+ {
+ case ' ':
+ if(i==0 || escapeSpace) {
+ buf.append('\\');
+ }
+ buf.append(' ');
+ break;
+
+ case '\\':
+ buf.append('\\').append('\\');
+ break;
+
+ case '\t':
+ buf.append('\\').append('t');
+ break;
+
+ case '\n':
+ buf.append('\\').append('n');
+ break;
+
+ case '\r':
+ buf.append('\\').append('r');
+ break;
+
+ case '\f':
+ buf.append('\\').append('f');
+ break;
+
+ default:
+ if((c<0x20)||(c>0x7e)) {
+ buf.append('\\').append('u');
+ buf.append(toHex((c >> 12) & 0xF));
+ buf.append(toHex((c >> 8) & 0xF));
+ buf.append(toHex((c >> 4) & 0xF));
+ buf.append(toHex( c & 0xF));
+ } else {
+ if (specialSaveChars.indexOf(c) != -1)
+ buf.append('\\');
+ buf.append(c);
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Convert a nibble to a hex character
+ * @param nibble the nibble to convert.
+ */
+ public static char toHex(int n) {
+ return hd[(n & 0xF)];
+ }
+
+ /** A table of hex digits */
+ private static final char[] hd = {
+ '0','1','2','3','4','5','6','7',
+ '8','9','A','B','C','D','E','F'
+ };
+}
\ No newline at end of file
--- /dev/null
+package uk.org.retep.util.misc;
+
+/**
+ * Similar to StringTokenizer but handles white spaces and multiple delimiters
+ * between tokens. It also handles quotes
+ *
+ * @author
+ * @version 1.0
+ */
+
+public class WStringTokenizer
+{
+ String string;
+ int pos,len;
+
+ /**
+ * Constructor
+ */
+ public WStringTokenizer()
+ {
+ }
+
+ /**
+ * Constructor: set the initial string
+ * @param aString String to tokenise
+ */
+ public WStringTokenizer(String aString)
+ {
+ setString(aString);
+ }
+
+ /**
+ * @param aString String to tokenise
+ */
+ public void setString(String aString)
+ {
+ string=aString;
+ pos=0;
+ len=string.length();
+ }
+
+ /**
+ * @return true if more tokens may be possible
+ */
+ public boolean hasMoreTokens()
+ {
+ return !(string==null || pos==len);
+ }
+
+ /**
+ * @return next token, null if complete.
+ */
+ public String nextToken()
+ {
+ char c;
+ boolean q=false;
+
+ if(!hasMoreTokens())
+ return null;
+
+ // find start of token
+ while(pos<len) {
+ c = string.charAt(pos);
+ if(c=='\'' || c=='\"')
+ q=!q;
+ if(q || c==' '||c=='\t')
+ pos++;
+ else
+ break;
+ }
+
+ // find last char of token
+ int p=pos;
+ while(pos<len) {
+ c = string.charAt(pos);
+ if(c=='\'' || c=='\"')
+ q=!q;
+ if(!q && (c==' '||c=='\t') )
+ break;
+ else
+ pos++;
+ }
+
+ return string.substring(p,pos);
+ }
+
+ /**
+ * Compare a string against an array of strings and return the index
+ * @param t array to compare against (all lowercase)
+ * @param s string to test
+ * @return index in t of s, -1 if not present
+ */
+ public static int matchToken(String[] t,String s)
+ {
+ s=s.toLowerCase();
+ for(int i=0;i<t.length;i++)
+ if(t[i].equals(s))
+ return i;
+ return -1;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package uk.org.retep.util.models;
+
+import uk.org.retep.util.hba.Record;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import javax.swing.table.*;
+
+/**
+ * A TableModel to display the contents of a pg_hba.conf file
+ * @author
+ * @version 1.0
+ */
+
+public class HBATableModel extends AbstractTableModel
+{
+ ArrayList list = new ArrayList();
+
+ private static final String cols[] = {
+ "Type","Database","IP Address","IP Mask","Authentication","Arguments"
+ };
+
+
+ public HBATableModel()
+ {
+ }
+
+ public ArrayList getArray()
+ {
+ return list;
+ }
+
+ public int getColumnCount()
+ {
+ return cols.length;
+ }
+
+ public Object getValueAt(int aRow, int aCol)
+ {
+ Record rec = (Record) list.get(aRow);
+ int t;
+
+ switch(aCol)
+ {
+ case 0:
+ t = rec.getType();
+ return t<0 ? "ERR" : Record.types[t] ;
+
+ case 1:
+ return rec.getDatabase();
+
+ case 2:
+ return rec.getIP();
+
+ case 3:
+ return rec.getMask();
+
+ case 4:
+ t=rec.getAuthType();
+ return t<0 ? "ERR" : Record.auths[t] ;
+
+ case 5:
+ return rec.getAuthArgs();
+
+ default:
+ return "";
+ }
+ }
+
+ public int getRowCount()
+ {
+ return list.size();
+ }
+
+ public boolean isCellEditable(int rowIndex, int columnIndex)
+ {
+ /**@todo: Override this javax.swing.table.AbstractTableModel method*/
+ return super.isCellEditable( rowIndex, columnIndex);
+ }
+
+ public String getColumnName(int aColumn)
+ {
+ return cols[aColumn];
+ }
+
+ public void setValueAt(Object aValue, int rowIndex, int columnIndex)
+ {
+ /**@todo: Override this javax.swing.table.AbstractTableModel method*/
+ super.setValueAt( aValue, rowIndex, columnIndex);
+ }
+}
\ No newline at end of file
--- /dev/null
+package uk.org.retep.util.models;
+
+import uk.org.retep.util.Logger;
+import uk.org.retep.util.misc.PropertiesIO;
+
+import java.io.*;
+import java.util.*;
+import javax.swing.table.*;
+
+import java.text.*;
+
+/**
+ * A TableModel that shows a view of a PropertyFile object
+ *
+ * $Id: PropertiesTableModel.java,v 1.1 2001/03/05 09:15:37 peter Exp $
+ *
+ * @author
+ * @version 1.0
+ */
+public class PropertiesTableModel extends AbstractTableModel
+{
+ // The properties
+ protected TreeMap properties;
+ protected Properties origProperties;
+ protected Object keys[];
+
+ public PropertiesTableModel()
+ {
+ this(new Properties());
+ }
+
+ public PropertiesTableModel(Properties aProperties)
+ {
+ setProperties(aProperties);
+ }
+
+ public synchronized int getKeyRow(Object k)
+ {
+ for(int i=0;i<keys.length;i++) {
+ if(keys[i].equals(k)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Best not use this one to update, use the put method in this class!
+ */
+ public Properties getProperties()
+ {
+ return origProperties;
+ }
+
+ public synchronized void put(Object k,Object v)
+ {
+ properties.put(k,v);
+ origProperties.put(k,v);
+ refresh();
+ }
+
+ public Object get(Object k)
+ {
+ return origProperties.get(k);
+ }
+
+ public synchronized void remove(Object k)
+ {
+ properties.remove(k);
+ origProperties.remove(k);
+ refresh();
+ }
+
+ public boolean contains(Object o)
+ {
+ return origProperties.contains(o);
+ }
+
+ public boolean containsKey(Object o)
+ {
+ return origProperties.containsKey(o);
+ }
+
+ public boolean containsValue(Object o)
+ {
+ return origProperties.containsValue(o);
+ }
+
+ public void setProperties(Properties aProperties)
+ {
+ origProperties=aProperties;
+ properties = PropertiesIO.getTreeMap(aProperties);
+ refresh();
+ }
+
+ public void refresh()
+ {
+ keys = properties.keySet().toArray();
+ fireTableDataChanged();
+ }
+
+ private static final String cols[] = {
+ "Property","Value"
+ };
+
+ public int getColumnCount()
+ {
+ return cols.length;
+ }
+
+ public Object getValueAt(int aRow, int aColumn)
+ {
+ if(aRow<0 || aRow>=keys.length || aColumn<0 || aColumn>=cols.length)
+ return null;
+
+ Object key = keys[aRow];
+
+ switch(aColumn)
+ {
+ case 0:
+ return key;
+
+ case 1:
+ return properties.get(key);
+
+ default:
+ return null;
+ }
+ }
+
+ public int getRowCount()
+ {
+ return keys.length;
+ }
+
+ public String getColumnName(int aColumn)
+ {
+ return cols[aColumn];
+ }
+
+ public void setValueAt(Object aValue, int aRow, int aColumn)
+ {
+ if(aRow<0 || aRow>=keys.length || aColumn<0 || aColumn>=cols.length)
+ return;
+
+ switch(aColumn)
+ {
+ // Rename the key (only if not already present). If already present
+ // the refresh() will replace with the old one anyhow...
+ case 0:
+ if(!properties.containsKey(aValue)) {
+ Object oldValue = get(keys[aRow]);
+ remove(keys[aRow]);
+ put(aValue,oldValue);
+ }
+ refresh();
+ break;
+
+ // Update the value...
+ case 1:
+ put(keys[aRow],aValue);
+ //refresh();
+ break;
+
+ default:
+ // Should never be called
+ Logger.log(Logger.ERROR,"PropertiesTableModel: Column range",aColumn);
+ }
+ }
+
+ public boolean isCellEditable(int aRow, int aColumn)
+ {
+ return true;
+ }
+
+}
--- /dev/null
+package uk.org.retep.util.proped;
+
+import uk.org.retep.util.ExceptionDialog;
+import uk.org.retep.util.Globals;
+import uk.org.retep.util.Logger;
+import uk.org.retep.util.StandaloneApp;
+
+import java.io.IOException;
+import java.util.Iterator;
+import javax.swing.JComponent;
+
+/**
+ * Standalone entry point for the Properties editor
+ *
+ * $Id: Main.java,v 1.1 2001/03/05 09:15:38 peter Exp $
+ */
+
+public class Main extends StandaloneApp
+{
+ public Main(String[] args)
+ throws Exception
+ {
+ super(args);
+ }
+
+ public JComponent init()
+ throws Exception
+ {
+ Globals globals = Globals.getInstance();
+
+ PropertyEditor panel = new PropertyEditor();
+
+ // Only handle 1 open at a time in standalone mode
+ if(globals.getArgumentCount()>0) {
+ try {
+ panel.openFile(globals.getArgument(0));
+ } catch(IOException ioe) {
+ ExceptionDialog.displayException(ioe,"while loading "+globals.getArgument(0));
+ throw (Exception) ioe.fillInStackTrace();
+ }
+ }
+
+ return panel;
+ }
+
+ public static void main(String[] args)
+ throws Exception
+ {
+ Main main = new Main(args);
+ main.pack();
+ main.setVisible(true);
+ }
+}
\ No newline at end of file
--- /dev/null
+package uk.org.retep.util.proped;
+
+import uk.org.retep.util.ExceptionDialog;
+import uk.org.retep.util.misc.PropertiesIO;
+import uk.org.retep.util.models.PropertiesTableModel;
+
+import java.awt.*;
+import java.io.*;
+import java.util.*;
+import javax.swing.*;
+import java.awt.event.*;
+
+/**
+ * A property file editor
+ *
+ * $Id: PropertyEditor.java,v 1.1 2001/03/05 09:15:38 peter Exp $
+ *
+ * @author
+ * @version 1.0
+ */
+
+public class PropertyEditor
+extends JPanel
+implements uk.org.retep.tools.Tool
+{
+ BorderLayout borderLayout1 = new BorderLayout();
+
+ // The filename, null if not set
+ String filename;
+ File file;
+
+ JScrollPane jScrollPane1 = new JScrollPane();
+ JTable contentTable = new JTable();
+
+ PropertiesTableModel model = new PropertiesTableModel();
+
+ boolean standaloneMode;
+
+ private static final String TITLE_PREFIX = "Retep PropertyEditor";
+ JPopupMenu popupMenu = new JPopupMenu();
+ JMenuItem newPopupItem = new JMenuItem();
+ JMenuItem dupPopupItem = new JMenuItem();
+ JMenuItem delPopupItem = new JMenuItem();
+ JMenuBar menuBar = new JMenuBar();
+ JMenu jMenu1 = new JMenu();
+ JMenuItem jMenuItem4 = new JMenuItem();
+ JMenuItem jMenuItem5 = new JMenuItem();
+ JMenuItem jMenuItem6 = new JMenuItem();
+ JMenuItem jMenuItem7 = new JMenuItem();
+ JMenuItem jMenuItem8 = new JMenuItem();
+ JMenuItem closeMenuItem = new JMenuItem();
+
+ public PropertyEditor()
+ {
+ try
+ {
+ jbInit();
+ }
+ catch(Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * @return the default menubar
+ */
+ public JMenuBar getMenuBar()
+ {
+ return menuBar;
+ }
+
+ /**
+ * @return the File menu
+ */
+ public JMenu getMenu()
+ {
+ return jMenu1;
+ }
+
+ /**
+ * @return the recomended title string for the parent JFrame/JInternalFrame
+ */
+ public String getTitle()
+ {
+ if(filename==null) {
+ return TITLE_PREFIX;
+ }
+ return TITLE_PREFIX+": "+filename;
+ }
+
+ /**
+ * Sets menus up to Standalone mode
+ */
+ public void setStandaloneMode(boolean aMode)
+ {
+ standaloneMode=aMode;
+ if(aMode) {
+ closeMenuItem.setText("Exit");
+ } else {
+ closeMenuItem.setText("Close");
+ }
+ }
+
+ public boolean isStandalone()
+ {
+ return standaloneMode;
+ }
+
+ public void openFile(String aFile)
+ throws IOException
+ {
+ openFile(new File(aFile));
+ }
+
+ public void openFile(File aFile)
+ throws IOException
+ {
+ FileInputStream fis = new FileInputStream(aFile);
+ Properties p = new Properties();
+ p.load(fis);
+ fis.close();
+ model.setProperties(p);
+
+ file=aFile;
+ filename = aFile.getAbsolutePath();
+ }
+
+ public void saveFile(File aFile)
+ throws IOException
+ {
+ FileOutputStream fis = new FileOutputStream(aFile);
+ PropertiesIO.save(model.getProperties(),fis,'=',"Written by "+TITLE_PREFIX);
+ fis.close();
+
+ filename = aFile.getAbsolutePath();
+ file = aFile;
+ }
+
+ void jbInit() throws Exception
+ {
+ this.setLayout(borderLayout1);
+ contentTable.setToolTipText("");
+ contentTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
+ contentTable.setModel(model);
+ contentTable.addMouseListener(new java.awt.event.MouseAdapter()
+ {
+ public void mouseClicked(MouseEvent e)
+ {
+ contentTable_mouseClicked(e);
+ }
+ public void mouseReleased(MouseEvent e)
+ {
+ contentTable_mouseReleased(e);
+ }
+ });
+ newPopupItem.setText("New");
+ newPopupItem.addActionListener(new java.awt.event.ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ newPopupItem_actionPerformed(e);
+ }
+ });
+ dupPopupItem.setText("Duplicate");
+ dupPopupItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(67, java.awt.event.KeyEvent.CTRL_MASK, false));
+ dupPopupItem.addActionListener(new java.awt.event.ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ dupPopupItem_actionPerformed(e);
+ }
+ });
+ delPopupItem.setText("Delete");
+ delPopupItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(68, java.awt.event.KeyEvent.CTRL_MASK, false));
+ delPopupItem.addActionListener(new java.awt.event.ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ delPopupItem_actionPerformed(e);
+ }
+ });
+ jMenu1.setText("File");
+ jMenuItem4.setText("Open");
+ jMenuItem4.addActionListener(new java.awt.event.ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ jMenuItem4_actionPerformed(e);
+ }
+ });
+ jMenuItem5.setText("Save");
+ jMenuItem5.addActionListener(new java.awt.event.ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ jMenuItem5_actionPerformed(e);
+ }
+ });
+ jMenuItem6.setText("Save As");
+ jMenuItem6.addActionListener(new java.awt.event.ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ jMenuItem6_actionPerformed(e);
+ }
+ });
+ jMenuItem7.setText("Revert");
+ jMenuItem7.addActionListener(new java.awt.event.ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ jMenuItem7_actionPerformed(e);
+ }
+ });
+ jMenuItem8.setText("Print");
+ closeMenuItem.setText("Close");
+ closeMenuItem.addActionListener(new java.awt.event.ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ closeMenuItem_actionPerformed(e);
+ }
+ });
+ jMenu2.setText("Edit");
+ jMenuItem1.setText("New");
+ jMenuItem1.setAccelerator(javax.swing.KeyStroke.getKeyStroke(78, java.awt.event.KeyEvent.CTRL_MASK, false));
+ jMenuItem1.addActionListener(new java.awt.event.ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ newPopupItem_actionPerformed(e);
+ }
+ });
+ jMenuItem2.setText("Duplicate");
+ jMenuItem3.setText("Delete");
+ this.add(jScrollPane1, BorderLayout.CENTER);
+ jScrollPane1.getViewport().add(contentTable, null);
+ popupMenu.add(newPopupItem);
+ popupMenu.add(dupPopupItem);
+ popupMenu.add(delPopupItem);
+ menuBar.add(jMenu1);
+ menuBar.add(jMenu2);
+ jMenu1.add(jMenuItem4);
+ jMenu1.add(jMenuItem5);
+ jMenu1.add(jMenuItem6);
+ jMenu1.add(jMenuItem7);
+ jMenu1.addSeparator();
+ jMenu1.add(jMenuItem8);
+ jMenu1.addSeparator();
+ jMenu1.add(closeMenuItem);
+ jMenu2.add(jMenuItem1);
+ jMenu2.add(jMenuItem2);
+ jMenu2.add(jMenuItem3);
+ }
+
+ Point popupPoint = new Point();
+ JMenu jMenu2 = new JMenu();
+ JMenuItem jMenuItem1 = new JMenuItem();
+ JMenuItem jMenuItem2 = new JMenuItem();
+ JMenuItem jMenuItem3 = new JMenuItem();
+ void contentTable_mouseClicked(MouseEvent e)
+ {
+ if(e.isPopupTrigger()) {
+ popupPoint.setLocation(e.getX(),e.getY());
+ popupMenu.show(contentTable,e.getX(),e.getY());
+ }
+ }
+
+ void contentTable_mouseReleased(MouseEvent e)
+ {
+ contentTable_mouseClicked(e);
+ }
+
+ void jMenuItem4_actionPerformed(ActionEvent e)
+ {
+ JFileChooser fc = new JFileChooser();
+ if(fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
+ try {
+ openFile(fc.getSelectedFile());
+ } catch(IOException ioe) {
+ ExceptionDialog.displayException(ioe);
+ }
+ }
+ }
+
+ void closeMenuItem_actionPerformed(ActionEvent e)
+ {
+ if(standaloneMode) {
+ System.exit(0);
+ } else {
+ filename="";
+ file=null;
+ model.setProperties(new Properties());
+ }
+ }
+
+ void newPopupItem_actionPerformed(ActionEvent e)
+ {
+ int y = contentTable.rowAtPoint(popupPoint);
+
+ // create a new unique key based on the current one
+ String key=(String) model.getValueAt(y,0);
+
+ if(key==null) {
+ key="new-key";
+ }
+
+ int uid=1;
+ while(model.containsKey(key+uid)) {
+ uid++;
+ }
+
+ key=key+uid;
+ model.put(key,"");
+ contentTable.clearSelection();
+ }
+
+ void dupPopupItem_actionPerformed(ActionEvent e)
+ {
+ int y = contentTable.rowAtPoint(popupPoint);
+
+ // create a new unique key based on the current one
+ String key=(String) model.getValueAt(y,0);
+ Object val=model.get(key);
+
+ int uid=1;
+ while(model.containsKey(key+uid)) {
+ uid++;
+ }
+
+ key=key+uid;
+ model.put(key,val);
+ contentTable.clearSelection();
+ }
+
+ void delPopupItem_actionPerformed(ActionEvent e)
+ {
+ int y = contentTable.rowAtPoint(popupPoint);
+ model.remove(model.getValueAt(y,0));
+ }
+
+ void jMenuItem6_actionPerformed(ActionEvent e)
+ {
+ JFileChooser fc = new JFileChooser();
+ if(fc.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
+ try {
+ saveFile(fc.getSelectedFile());
+ } catch(IOException ioe) {
+ ExceptionDialog.displayException(ioe);
+ }
+ }
+ }
+
+ void jMenuItem5_actionPerformed(ActionEvent e)
+ {
+ if(filename==null) {
+ jMenuItem6_actionPerformed(e);
+ } else {
+ try {
+ saveFile(file);
+ } catch(IOException ioe) {
+ ExceptionDialog.displayException(ioe);
+ }
+ }
+ }
+
+ void jMenuItem7_actionPerformed(ActionEvent e)
+ {
+ // add check here
+ if(file!=null) {
+ try {
+ openFile(file);
+ } catch(IOException ioe) {
+ ExceptionDialog.displayException(ioe);
+ }
+ } else {
+ jMenuItem4_actionPerformed(e);
+ }
+ }
+}
\ No newline at end of file