diff --git a/contrib/retep/CHANGELOG b/contrib/retep/CHANGELOG index 59f7c335cb9b5353e6db97725ba2d1bb51a5fbff..188c40129e20cfcd2c13d9f2f3ed688e2b04d997 100644 --- a/contrib/retep/CHANGELOG +++ b/contrib/retep/CHANGELOG @@ -1,4 +1,7 @@ +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. diff --git a/contrib/retep/Implementation b/contrib/retep/Implementation index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..b3125acf0edeb2f94639df1aea407be2d77d6928 100644 --- a/contrib/retep/Implementation +++ b/contrib/retep/Implementation @@ -0,0 +1,116 @@ +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. + diff --git a/contrib/retep/README b/contrib/retep/README index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5355c9d99f3c00bb51861e427128f16d24f5d6e4 100644 --- a/contrib/retep/README +++ b/contrib/retep/README @@ -0,0 +1,35 @@ +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. + diff --git a/contrib/retep/build.xml b/contrib/retep/build.xml index 019903bc798efb25a1fbe730fd217dd6031ef27a..78b45e3bf45e40230de9271d5bd80eafbb82332b 100644 --- a/contrib/retep/build.xml +++ b/contrib/retep/build.xml @@ -2,7 +2,7 @@ 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 $ --> @@ -35,16 +35,16 @@ </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> diff --git a/contrib/retep/retep.jpx b/contrib/retep/retep.jpx index 640df105046bfda860cb46f3e36d5229b2864e6a..746137dd3066736bf2c347bb5495dbf2e0985c9b 100644 --- a/contrib/retep/retep.jpx +++ b/contrib/retep/retep.jpx @@ -4,6 +4,12 @@ <project> <property category="idl" name="ProcessIDL" value="false" /> <property category="runtime.0" name="RunnableType" value="com.borland.jbuilder.runtime.ApplicationRunner" /> + <property category="runtime.0" name="application.class" value="uk.org.retep.util.hba.Main" /> + <property category="runtime.0" name="application.parameters" value="-d2 pg_hba.conf" /> + <property category="runtime.0" name="appserver.ejbJarsSaved" value="1" /> + <property category="runtime.0" name="appserver.parameters" value="-jts -jns -jss -jdb" /> + <property category="runtime.0" name="appserver.servername" value="ejbcontainer" /> + <property category="runtime.0" name="appserver.vmparameters" value="" /> <property category="runtime.0" name="jsprunner.docbase" value="." /> <property category="runtime.0" name="jsprunner.jspfile" value="E%|/docs/java/xml/example6" /> <property category="sys" name="AuthorLabel" value="@author" /> @@ -15,7 +21,7 @@ <property category="sys" name="CompanyLabel" value="Company:" /> <property category="sys" name="Copyright" value="Copyright (c) 2001" /> <property category="sys" name="CopyrightLabel" value="Copyright:" /> - <property category="sys" name="DefaultPackage" value="uk.org.retep.xml.jdbc" /> + <property category="sys" name="DefaultPackage" value="uk.org.retep.util.misc" /> <property category="sys" name="Description" value="" /> <property category="sys" name="DescriptionLabel" value="Description:" /> <property category="sys" name="DocPath" value="doc" /> @@ -25,7 +31,7 @@ <property category="sys" name="InstanceVisibility" value="0" /> <property category="sys" name="JDK" value="java 1.3.0-C" /> <property category="sys" name="LastTag" value="0" /> - <property category="sys" name="Libraries" value="JAXP;Oracle JDBC" /> + <property category="sys" name="Libraries" value="JAXP;Oracle JDBC;JDK1.3 JRE" /> <property category="sys" name="MakeStable" value="0" /> <property category="sys" name="OutPath" value="build" /> <property category="sys" name="SourcePath" value="." /> @@ -34,13 +40,17 @@ <property category="sys" name="Version" value="1.0" /> <property category="sys" name="VersionLabel" value="@version" /> <property category="sys" name="WorkingDirectory" value="." /> - <node type="Folder" name="core" /> + <node type="Package" name="uk.org.retep.app" /> + <node type="Package" name="uk.org.retep.dtu" /> + <node type="Package" name="uk.org.retep.tools" /> + <node type="Package" name="uk.org.retep.util" /> <node type="Package" name="uk.org.retep.xml.core" /> <node type="Package" name="uk.org.retep.xml.jdbc" /> <node type="Package" name="uk.org.retep.xml.parser" /> <file path="build.xml" /> <file path="CHANGELOG" /> <file path="Implementation" /> + <file path="uk/org/retep/util/models/PropertiesTableModel.java" /> <file path="README" /> </project> diff --git a/contrib/retep/uk/org/retep/dtu/DCollection.java b/contrib/retep/uk/org/retep/dtu/DCollection.java new file mode 100644 index 0000000000000000000000000000000000000000..e97fc067c43ffd43c11a56e65929ad29bed997cd --- /dev/null +++ b/contrib/retep/uk/org/retep/dtu/DCollection.java @@ -0,0 +1,228 @@ +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 diff --git a/contrib/retep/uk/org/retep/dtu/DConstants.java b/contrib/retep/uk/org/retep/dtu/DConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..fb825d735bf60b9dd239363d471140ad8620acb5 --- /dev/null +++ b/contrib/retep/uk/org/retep/dtu/DConstants.java @@ -0,0 +1,43 @@ +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 diff --git a/contrib/retep/uk/org/retep/dtu/DElement.java b/contrib/retep/uk/org/retep/dtu/DElement.java new file mode 100644 index 0000000000000000000000000000000000000000..73fa7e87e83ec1f43bbb0c16fc8c3ba14d73b2e1 --- /dev/null +++ b/contrib/retep/uk/org/retep/dtu/DElement.java @@ -0,0 +1,31 @@ +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 diff --git a/contrib/retep/uk/org/retep/dtu/DEnvironment.java b/contrib/retep/uk/org/retep/dtu/DEnvironment.java new file mode 100644 index 0000000000000000000000000000000000000000..2efcfe81a1ed8bdf0357384b17f610fc0de04a43 --- /dev/null +++ b/contrib/retep/uk/org/retep/dtu/DEnvironment.java @@ -0,0 +1,30 @@ +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 diff --git a/contrib/retep/uk/org/retep/dtu/DModule.java b/contrib/retep/uk/org/retep/dtu/DModule.java new file mode 100644 index 0000000000000000000000000000000000000000..21d037f2678c0f4a0ba4dce67939ad31cc360f9f --- /dev/null +++ b/contrib/retep/uk/org/retep/dtu/DModule.java @@ -0,0 +1,97 @@ +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 diff --git a/contrib/retep/uk/org/retep/dtu/DModuleXML.java b/contrib/retep/uk/org/retep/dtu/DModuleXML.java new file mode 100644 index 0000000000000000000000000000000000000000..31552cd8d721823af1573ab16a4a4b58a8187245 --- /dev/null +++ b/contrib/retep/uk/org/retep/dtu/DModuleXML.java @@ -0,0 +1,233 @@ +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 diff --git a/contrib/retep/uk/org/retep/dtu/DNode.java b/contrib/retep/uk/org/retep/dtu/DNode.java new file mode 100644 index 0000000000000000000000000000000000000000..7a8321741cdd325fcff99c8364738f71c8e40176 --- /dev/null +++ b/contrib/retep/uk/org/retep/dtu/DNode.java @@ -0,0 +1,233 @@ +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; + } + } + } + +} diff --git a/contrib/retep/uk/org/retep/dtu/DProcessor.java b/contrib/retep/uk/org/retep/dtu/DProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..7745a184840eafc61457e30a19a37f22fabee3cd --- /dev/null +++ b/contrib/retep/uk/org/retep/dtu/DProcessor.java @@ -0,0 +1,191 @@ +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 diff --git a/contrib/retep/uk/org/retep/dtu/DTransform.java b/contrib/retep/uk/org/retep/dtu/DTransform.java new file mode 100644 index 0000000000000000000000000000000000000000..88aac19768a1db857dc31111561c3d4b4daf0011 --- /dev/null +++ b/contrib/retep/uk/org/retep/dtu/DTransform.java @@ -0,0 +1,133 @@ +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 diff --git a/contrib/retep/uk/org/retep/tools.properties b/contrib/retep/uk/org/retep/tools.properties new file mode 100644 index 0000000000000000000000000000000000000000..38dc668aea164d15371acdfa5acb0a2df9c4cbc4 --- /dev/null +++ b/contrib/retep/uk/org/retep/tools.properties @@ -0,0 +1,8 @@ +#Written by Retep PropertyEditor +#Sat Mar 03 16:29:44 GMT+00:00 2001 +tool.hba=pg_hba.conf editor +tool.hba.class=uk.org.retep.util.hba.Editor +tool.proped.class=uk.org.retep.util.proped.PropertyEditor +tool.hba.type=Misc +tool.proped.type=Misc +tool.proped=Properties Editor diff --git a/contrib/retep/uk/org/retep/tools/Tool.java b/contrib/retep/uk/org/retep/tools/Tool.java new file mode 100644 index 0000000000000000000000000000000000000000..ffd19ae90c0cd686e505080692b5c8a3e8e9aeec --- /dev/null +++ b/contrib/retep/uk/org/retep/tools/Tool.java @@ -0,0 +1,33 @@ +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 diff --git a/contrib/retep/uk/org/retep/util/ExceptionDialog.java b/contrib/retep/uk/org/retep/util/ExceptionDialog.java new file mode 100644 index 0000000000000000000000000000000000000000..ea3d5c43c981fbde2c11bee020d9387cbff65586 --- /dev/null +++ b/contrib/retep/uk/org/retep/util/ExceptionDialog.java @@ -0,0 +1,141 @@ +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 diff --git a/contrib/retep/uk/org/retep/util/Globals.java b/contrib/retep/uk/org/retep/util/Globals.java new file mode 100644 index 0000000000000000000000000000000000000000..c0e3548a321decafbee6272a7156183ad5a29a3b --- /dev/null +++ b/contrib/retep/uk/org/retep/util/Globals.java @@ -0,0 +1,170 @@ +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 diff --git a/contrib/retep/uk/org/retep/util/Logger.java b/contrib/retep/uk/org/retep/util/Logger.java new file mode 100644 index 0000000000000000000000000000000000000000..c272f1d005aa78d2b8abdb8d5c811af061df4018 --- /dev/null +++ b/contrib/retep/uk/org/retep/util/Logger.java @@ -0,0 +1,150 @@ +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(); + } + +} diff --git a/contrib/retep/uk/org/retep/util/Main.java b/contrib/retep/uk/org/retep/util/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..5df75e080245771d1bce56c03e46263f8b45bb32 --- /dev/null +++ b/contrib/retep/uk/org/retep/util/Main.java @@ -0,0 +1,42 @@ +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); + } +} diff --git a/contrib/retep/uk/org/retep/util/StandaloneApp.java b/contrib/retep/uk/org/retep/util/StandaloneApp.java new file mode 100644 index 0000000000000000000000000000000000000000..3c6c9aca367a11800d964841437f29a13508a775 --- /dev/null +++ b/contrib/retep/uk/org/retep/util/StandaloneApp.java @@ -0,0 +1,79 @@ +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 diff --git a/contrib/retep/uk/org/retep/util/hba/Editor.java b/contrib/retep/uk/org/retep/util/hba/Editor.java new file mode 100644 index 0000000000000000000000000000000000000000..6571bfebb7a859e4da3f5361d75881da5c51da45 --- /dev/null +++ b/contrib/retep/uk/org/retep/util/hba/Editor.java @@ -0,0 +1,141 @@ +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 diff --git a/contrib/retep/uk/org/retep/util/hba/Main.java b/contrib/retep/uk/org/retep/util/hba/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..3d9343140b6d6674105e7fdc5631f985d84aa568 --- /dev/null +++ b/contrib/retep/uk/org/retep/util/hba/Main.java @@ -0,0 +1,47 @@ +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); + } +} diff --git a/contrib/retep/uk/org/retep/util/hba/Record.java b/contrib/retep/uk/org/retep/util/hba/Record.java new file mode 100644 index 0000000000000000000000000000000000000000..b91e6dc49df0a9f9374df0dc90156b7d09981cdc --- /dev/null +++ b/contrib/retep/uk/org/retep/util/hba/Record.java @@ -0,0 +1,238 @@ +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 diff --git a/contrib/retep/uk/org/retep/util/misc/IPAddress.java b/contrib/retep/uk/org/retep/util/misc/IPAddress.java new file mode 100644 index 0000000000000000000000000000000000000000..a04babde3a277e67d9a475961293de4ae96559dd --- /dev/null +++ b/contrib/retep/uk/org/retep/util/misc/IPAddress.java @@ -0,0 +1,125 @@ +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 diff --git a/contrib/retep/uk/org/retep/util/misc/PropertiesIO.java b/contrib/retep/uk/org/retep/util/misc/PropertiesIO.java new file mode 100644 index 0000000000000000000000000000000000000000..7bed62c4d28a5e6bd845f497137c927398d4c805 --- /dev/null +++ b/contrib/retep/uk/org/retep/util/misc/PropertiesIO.java @@ -0,0 +1,157 @@ +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 diff --git a/contrib/retep/uk/org/retep/util/misc/WStringTokenizer.java b/contrib/retep/uk/org/retep/util/misc/WStringTokenizer.java new file mode 100644 index 0000000000000000000000000000000000000000..763676cfb3b75b8f8410205182cb61067cae0582 --- /dev/null +++ b/contrib/retep/uk/org/retep/util/misc/WStringTokenizer.java @@ -0,0 +1,102 @@ +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 diff --git a/contrib/retep/uk/org/retep/util/models/HBATableModel.java b/contrib/retep/uk/org/retep/util/models/HBATableModel.java new file mode 100644 index 0000000000000000000000000000000000000000..fb7bb72baf46bc9b749ad4afb6b80a8ecf32d329 --- /dev/null +++ b/contrib/retep/uk/org/retep/util/models/HBATableModel.java @@ -0,0 +1,91 @@ +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 diff --git a/contrib/retep/uk/org/retep/util/models/PropertiesTableModel.java b/contrib/retep/uk/org/retep/util/models/PropertiesTableModel.java new file mode 100644 index 0000000000000000000000000000000000000000..d48b166e9cd4661ffb70573ddbe58a607fae9290 --- /dev/null +++ b/contrib/retep/uk/org/retep/util/models/PropertiesTableModel.java @@ -0,0 +1,176 @@ +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; + } + +} diff --git a/contrib/retep/uk/org/retep/util/proped/Main.java b/contrib/retep/uk/org/retep/util/proped/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..6f2c73bc68ff4f53a3249c4db8be8c49ae7844c8 --- /dev/null +++ b/contrib/retep/uk/org/retep/util/proped/Main.java @@ -0,0 +1,53 @@ +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 diff --git a/contrib/retep/uk/org/retep/util/proped/PropertyEditor.java b/contrib/retep/uk/org/retep/util/proped/PropertyEditor.java new file mode 100644 index 0000000000000000000000000000000000000000..b5c19e10876f444b93ae4c81e442c4468f8a6ede --- /dev/null +++ b/contrib/retep/uk/org/retep/util/proped/PropertyEditor.java @@ -0,0 +1,381 @@ +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