It's surprisingly easy to create a simple custom EOAdaptor. You can look at JavaFSAdaptor, which is part of Project Wonder, and you can also look at JavaMemoryAdaptor, also in Wonder.

Here is a little example on how the create an EOAdaptor:

Creating an EOAdaptor can be very straightforward (or very complicated)
depending on the underlying data source. In this example, and for the
sake of keeping thing simple, I'm using the file system as a data source
(well, at least, how java.io.File sees it).
At a bare minimum you need to implement three classes: EOAdaptor,
EOAdaptorContext, EOAdaptorChannel.

[EOAdaptor JavaDoc]

Not much in here. The EOAdaptor is mostly a rendezvous point to access
other functionality in your adaptor. You need to implement a couple of
abstract methods though:

In the case of a file system there is not much to do:

public void assertConnectionDictionaryIsValid()
	{
	}

	public EOAdaptorContext createAdaptorContext()
	{
		return new FSAdaptorContext( this );
	}

	public Class defaultExpressionClass()
	{
		throw new UnsupportedOperationException
( "FSAdaptor.defaultExpressionClass" );
	}

	public EOSQLExpressionFactory expressionFactory()
	{
		throw new UnsupportedOperationException
( "FSAdaptor.expressionFactory" );
	}

	public boolean isValidQualifierType(String aTypeName, EOModel aModel)
	{
		return true;
	}

	public EOSchemaGeneration synchronizationFactory()
	{
		throw new UnsupportedOperationException
( "FSAdaptor.synchronizationFactory" );
	}

The only important method is createAdaptorContext().

[EOAdaptorContext JavaDoc]

The adaptor context handles "transactions". It's usually pretty simple
if your data store support that concept. Not much to do here for a file
system:

public void beginTransaction()
	{
		if ( _hasTransaction == false )
		{
			_hasTransaction = true;

			this.transactionDidBegin();
		}
	}

	public void commitTransaction()
	{
		if ( _hasTransaction == true )
		{
			_hasTransaction = false;

			this.transactionDidCommit();
		}
	}

	public EOAdaptorChannel createAdaptorChannel()
	{
		return new FSAdaptorChannel( this );
	}

	public void handleDroppedConnection()
	{
	}

	public void rollbackTransaction()
	{
		throw new UnsupportedOperationException
( "FSAdaptorContext.rollbackTransaction" );
	}

Again createAdaptorChannel() is the only method that you really care
about in this case.

[EOAdaptorChannel JavaDoc]

That's the real thing. It's where most of the work is done. It's
important to get this one right. Depending on your back end, it's simply
an exercise in translating EOQualifiers into something that your data
storage will understand. Here a the most important methods:

Finally, fetching "rows" is a two steps process: first you need to setup
the "context" of what you are going to fetch and then fetch it:

In the case of a file system here is what you could have:

That could be it. Nothing to involved. However there is two extra
methods that make sense to implement: describeTableNames and
describeModelWithTableNames.

This is very useful to get you started in EOModeler instead of having to
create the entire model by hand. In the case of a file system, there is
only one model:

public EOModel describeModelWithTableNames(NSArray anArray)
	{
		return new EOModel( this.defaultModelPath() );
	}

And voilà. No you have a toy file system adaptor (aka JavaFSAdaptor).

Once you have your qualifier, simply build a fetch specification for it
and get your "file" EOs:

EOFetchSpecification	aFetchSpecification = new EOFetchSpecification
( "FSDirectory", aQualifier, null );
NSArray				someObjects =
anEditingContext.objectsWithFetchSpecification( aFetchSpecification );

You can use FSFile to retrieve files only. FSDirectory for, er,
directories. And FSItem for both. FSDirectory have two handy
relationships: "files" and "directories". Check the attached model.