Index: trunk/.cvsignore
===================================================================
--- trunk/.cvsignore	(revision 4)
+++ trunk/.cvsignore	(revision 5)
@@ -2,2 +2,3 @@
 .classpath
 .project
+velocity.log
Index: trunk/project.xml
===================================================================
--- trunk/project.xml	(revision 4)
+++ trunk/project.xml	(revision 5)
@@ -31,5 +31,16 @@
   <inceptionYear>2005</inceptionYear>
   <package>ws.fugue88.jpath</package>
-  <description>This is a small library that facilitates interaction with instance managers whose signatures aren't known at compile time. It assumes certain naming conventions that the instance managers are supposed to follow in order to locate and invoke the appropriate methods.</description>
+  <description><![CDATA[This library allows access to graphs of Java objects by path-like strings.  As a short example, suppose you have the following:</p>
+<source>
+class A {
+	String s;
+}
+class B {
+	A x;
+}
+</source>
+<p>Then, given an instance of class B, the path "/x/s" would provide access to the string <var>s</var>.</p>
+
+<p>Early-nulls (if <var>x</var> were <code>null</code>) are also supported (a la Expression Language), but so is automatic graph construction.  In such a case, a new instances of A would have been assigned to <var>x</var> to access the (still <code>null</code>) string <var>s</var>.]]></description>
   <!-- the project home page -->
   <url>http://maven.apache.org/reference/plugins/examples/</url>
@@ -41,6 +52,6 @@
        scm:<system>:<system specific connection string> -->
   <repository>
-    <connection>scm:cvs:pserver:anoncvs@cvs.apache.org:/home/cvspublic:maven-plugins/examples</connection>
-    <url>http://cvs.apache.org/viewcvs/maven-plugins/examples/</url>
+    <connection>scm:cvs:ext:devel.home:/srv/cvs:jpathlib</connection>
+    <!-- url>http://cvs.apache.org/viewcvs/maven-plugins/examples/</url -->
   </repository>
   <developers>
Index: trunk/src/ws/fugue88/jpath/Context.java
===================================================================
--- trunk/src/ws/fugue88/jpath/Context.java	(revision 4)
+++ trunk/src/ws/fugue88/jpath/Context.java	(revision 5)
@@ -3,4 +3,8 @@
  *
  * $Log$
+ * Revision 1.3  2005/08/19 17:51:17  dsowen
+ * Fixed: NPEs when using accessors from paths, &c.  Included tests.
+ * Fixed: parsing exception wasn't very enlightening.
+ *
  * Revision 1.2  2005/08/12 19:01:46  dsowen
  * Feature: can automatically fill missing parts of a graph.
@@ -18,4 +22,5 @@
 package ws.fugue88.jpath;
 
+import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.util.Iterator;
@@ -34,10 +39,9 @@
 	{
 		Context current = this;
-		Accessor accessor = null;
-		for(Iterator i = path.iterator(); i.hasNext();) {
-			accessor = current.getTargetAccessor((PathPart)i.next());
+		for(Iterator i = path.parents().iterator(); i.hasNext();) {
+			Accessor accessor = current.getTargetAccessor((PathPart)i.next());
 			Object obj = accessor.getValue();
 			if(obj == null) {
-				if(_modifying && i.hasNext()) {
+				if(_modifying) {
 					obj = createBlank(accessor.getType());
 					accessor.setValue(obj);
@@ -48,5 +52,5 @@
 			current = new Context(obj, _binder, _modifying);
 		}
-		return accessor;
+		return current.getTargetAccessor(path.terminal());
 	}
 
@@ -109,7 +113,14 @@
 
 	private Object createBlank(final Class type) throws IllegalAccessException,
-			InstantiationException
+			InstantiationException, InvocationTargetException
 	{
-		return type.newInstance();
+		try {
+			final Constructor ctor = type.getDeclaredConstructor(null);
+			ctor.setAccessible(true);
+			return ctor.newInstance(null);
+		} catch(NoSuchMethodException e) {
+			throw new InstantiationException("Class " + type.getName()
+					+ " must have a no-argument constructor.");
+		}
 	}
 
Index: trunk/src/ws/fugue88/jpath/Path.java
===================================================================
--- trunk/src/ws/fugue88/jpath/Path.java	(revision 4)
+++ trunk/src/ws/fugue88/jpath/Path.java	(revision 5)
@@ -3,4 +3,8 @@
  *
  * $Log$
+ * Revision 1.2  2005/08/19 17:51:17  dsowen
+ * Fixed: NPEs when using accessors from paths, &c.  Included tests.
+ * Fixed: parsing exception wasn't very enlightening.
+ *
  * Revision 1.1  2005/08/03 00:35:29  dsowen
  * Initial commit.
@@ -51,5 +55,8 @@
 				pp = new StringSelector(unquote(m.group(1)));
 			} else {
-				throw new ParseException(null, -1);
+				throw new ParseException(
+						"Expected identifier or selector, found '"
+								+ (buff.length() > 40 ? buff.subSequence(0, 37)
+										+ "..." : buff) + "'.", -1);
 			}
 			_parts.add(pp);
@@ -63,7 +70,7 @@
 	}
 
-	public String terminal()
+	public PathPart terminal()
 	{
-		return (String)_parts.get(_parts.size() - 1);
+		return (PathPart)_parts.get(_parts.size() - 1);
 	}
 
Index: trunk/testsrc/ws/fugue88/ContextTest.java
===================================================================
--- trunk/testsrc/ws/fugue88/ContextTest.java	(revision 5)
+++ trunk/testsrc/ws/fugue88/ContextTest.java	(revision 5)
@@ -0,0 +1,191 @@
+/*
+ * Created on Aug 2, 2005
+ * 
+ * $Log$
+ * Revision 1.1  2005/08/19 17:51:17  dsowen
+ * Fixed: NPEs when using accessors from paths, &c.  Included tests.
+ * Fixed: parsing exception wasn't very enlightening.
+ *
+ * Revision 1.2  2005/08/12 19:01:46  dsowen
+ * Feature: can automatically fill missing parts of a graph.
+ *
+ * Revision 1.1  2005/08/03 00:35:29  dsowen
+ * Initial commit.
+ *
+ */
+package ws.fugue88;
+
+import java.util.HashMap;
+
+import junit.framework.TestCase;
+import ws.fugue88.jpath.Accessor;
+import ws.fugue88.jpath.Context;
+import ws.fugue88.jpath.ContextFactory;
+import ws.fugue88.jpath.Path;
+
+/**
+ * @author dsowen
+ */
+public class ContextTest extends TestCase {
+
+	public void testFieldIdentifier() throws Exception
+	{
+		class A {
+
+			String b = "test";
+		}
+		A a = new A();
+
+		ContextFactory factory = new ContextFactory();
+		Context context = factory.createContext(a);
+		assertSame(a.b, context.getTarget(new Path("/b")));
+
+		Accessor accessor = context.getTargetAccessor(new Path("/b"));
+		assertSame(a.b, accessor.getValue());
+		accessor.setValue("done");
+		assertEquals("done", accessor.getValue());
+		assertEquals("done", a.b);
+	}
+
+	public void testGetterIdentifier() throws Exception
+	{
+		class A {
+
+			String getB()
+			{
+				return "test";
+			}
+		}
+		A a = new A();
+
+		ContextFactory factory = new ContextFactory();
+		Context context = factory.createContext(a);
+		assertEquals("test", context.getTarget(new Path("/b")));
+		try {
+			context.getTargetAccessor(new Path("/b")).setValue("done");
+			assertTrue(false);
+		} catch(UnsupportedOperationException e) {}
+	}
+
+	public void testGetterSetterIdentifier() throws Exception
+	{
+		class A {
+
+			String getB()
+			{
+				return x;
+			}
+
+			void setB(String s)
+			{
+				x = s;
+			}
+
+			String x = "test";
+		}
+		A a = new A();
+
+		ContextFactory factory = new ContextFactory();
+		Context context = factory.createContext(a);
+		assertEquals("test", context.getTarget(new Path("/b")));
+		context.getTargetAccessor(new Path("/b")).setValue("done");
+		assertEquals("done", a.getB());
+	}
+
+	public void testKey() throws Exception
+	{
+		class A {
+
+			HashMap b = new HashMap();
+
+			A()
+			{
+				b.put("c", "test");
+			}
+		}
+		A a = new A();
+
+		ContextFactory factory = new ContextFactory();
+		Context context = factory.createContext(a);
+		assertEquals("test", context.getTarget(new Path("/b['c']")));
+		context.getTargetAccessor(new Path("/b['c']")).setValue("done");
+		assertEquals("done", a.b.get("c"));
+	}
+
+	public void testEarlyNull() throws Exception
+	{
+		class A {
+
+			String s;
+		}
+		class B {
+
+			A a;
+		}
+		B b = new B();
+
+		ContextFactory factory = new ContextFactory();
+		Context context = factory.createContext(b);
+		assertNull(context.getTarget(new Path("/a/s")));
+		assertNull(b.a);
+	}
+
+	static class A {
+
+		String s;
+	}
+
+	public void testAutoGraphTargets() throws Exception
+	{
+		class B {
+
+			A a;
+		}
+		B b = new B();
+
+		ContextFactory factory = new ContextFactory();
+		Context context = factory.createModifyingContext(b);
+		assertNull(context.getTarget(new Path("/a/s")));
+		assertNotNull(b.a);
+		assertNull(b.a.s);
+	}
+
+	public void testAutoGraphAccessors() throws Exception
+	{
+		class B {
+
+			A a;
+		}
+		B b = new B();
+
+		ContextFactory factory = new ContextFactory();
+		Context context = factory.createModifyingContext(b);
+		Accessor accessor = context.getTargetAccessor(new Path("/a/s"));
+		assertNotNull(b.a);
+		assertNull(b.a.s);
+	}
+
+	private static class PrivateClass {
+
+		String s;
+
+		private PrivateClass()
+		{
+		}
+	}
+
+	public void testConstructPrivate() throws Exception
+	{
+		class B {
+
+			PrivateClass x;
+		}
+		B b = new B();
+
+		ContextFactory factory = new ContextFactory();
+		Context context = factory.createModifyingContext(b);
+		assertNull(context.getTarget(new Path("/x/s")));
+		assertNotNull(b.x);
+		assertNull(b.x.s);
+	}
+}
Index: trunk/testsrc/ws/fugue88/jpath/ContextTest.java
===================================================================
--- trunk/testsrc/ws/fugue88/jpath/ContextTest.java	(revision 4)
+++ 	(revision )
@@ -1,144 +1,0 @@
-/*
- * Created on Aug 2, 2005
- * 
- * $Log$
- * Revision 1.2  2005/08/12 19:01:46  dsowen
- * Feature: can automatically fill missing parts of a graph.
- *
- * Revision 1.1  2005/08/03 00:35:29  dsowen
- * Initial commit.
- *
- */
-package ws.fugue88.jpath;
-
-import java.util.HashMap;
-
-import junit.framework.TestCase;
-
-/**
- * @author dsowen
- */
-public class ContextTest extends TestCase {
-
-	public void testFieldIdentifier() throws Exception
-	{
-		class A {
-
-			String b = "test";
-		}
-		A a = new A();
-
-		ContextFactory factory = new ContextFactory();
-		Context context = factory.createContext(a);
-		assertSame(a.b, context.getTarget(new Path("/b")));
-
-		Accessor accessor = context.getTargetAccessor(new Path("/b"));
-		assertSame(a.b, accessor.getValue());
-		accessor.setValue("done");
-		assertEquals("done", accessor.getValue());
-		assertEquals("done", a.b);
-	}
-
-	public void testGetterIdentifier() throws Exception
-	{
-		class A {
-
-			String getB()
-			{
-				return "test";
-			}
-		}
-		A a = new A();
-
-		ContextFactory factory = new ContextFactory();
-		Context context = factory.createContext(a);
-		assertEquals("test", context.getTarget(new Path("/b")));
-		try {
-			context.getTargetAccessor(new Path("/b")).setValue("done");
-			assertTrue(false);
-		} catch(UnsupportedOperationException e) {}
-	}
-
-	public void testGetterSetterIdentifier() throws Exception
-	{
-		class A {
-
-			String getB()
-			{
-				return x;
-			}
-
-			void setB(String s)
-			{
-				x = s;
-			}
-
-			String x = "test";
-		}
-		A a = new A();
-
-		ContextFactory factory = new ContextFactory();
-		Context context = factory.createContext(a);
-		assertEquals("test", context.getTarget(new Path("/b")));
-		context.getTargetAccessor(new Path("/b")).setValue("done");
-		assertEquals("done", a.getB());
-	}
-
-	public void testKey() throws Exception
-	{
-		class A {
-
-			HashMap b = new HashMap();
-
-			A()
-			{
-				b.put("c", "test");
-			}
-		}
-		A a = new A();
-
-		ContextFactory factory = new ContextFactory();
-		Context context = factory.createContext(a);
-		assertEquals("test", context.getTarget(new Path("/b['c']")));
-		context.getTargetAccessor(new Path("/b['c']")).setValue("done");
-		assertEquals("done", a.b.get("c"));
-	}
-
-	public void testEarlyNull() throws Exception
-	{
-		class A {
-
-			String s;
-		}
-		class B {
-
-			A a;
-		}
-		B b = new B();
-
-		ContextFactory factory = new ContextFactory();
-		Context context = factory.createContext(b);
-		assertNull(context.getTarget(new Path("/a/s")));
-		assertNull(b.a);
-	}
-
-	static class A {
-
-		String s;
-	}
-
-	public void testAutoGraph() throws Exception
-	{
-		class B {
-
-			A a;
-		}
-		B b = new B();
-
-		ContextFactory factory = new ContextFactory();
-		Context context = factory.createModifyingContext(b);
-		assertNull(context.getTarget(new Path("/a/s")));
-		assertNotNull(b.a);
-		assertNull(b.a.s);
-	}
-}
