2012/04/08

space listing - the future - part 1

My schedule has been a little hectic the last couple of weeks, but this afternoon I finally found time to revisit the Space Listing. So, without further ado, here's the rootspace that contains my version of it :

<rootspace
  name="System Accessors"
  public="true"
  uri="urn:org:elbeesee:ext:system:accessors">
    
  <accessor>
    <id>system.SpaceStaticResourceList.accessor</id>
    <class>org.elbeesee.ext.system.SpaceStaticResourceListAccessor</class>
    <grammar>
      <active>
        <identifier>active:ssrls</identifier>
        <argument name="space" desc="space identifier (urn)"/>
        <argument name="version" min="0" max="1" desc="space version"/>
      </active>
    </grammar>
  </accessor>
    
  <fileset>
    <regex>res:/resources/stylesheets/.*</regex>
  </fileset>

  <import>
    <uri>urn:org:netkernel:xml:core</uri>
    <private/>
  </import>
    
  <import>
    <uri>urn:org:netkernel:ext:layer1</uri>
    <private/>
  </import>
</rootspace>

Nothing special there, same definition as the original active:sls. It's in the code that things are different, so lets have a look at that :

package org.elbeesee.ext.system;

// Author: Tom Geudens
// Date  : 2012/04/08

// The usual suspects for an accessor.
import org.netkernel.layer0.nkf.*;
import org.netkernel.layer0.meta.impl.SourcedArgumentMetaImpl;
import org.netkernel.module.standard.endpoint.StandardAccessorImpl;

// Processing.
import java.io.File;
import java.net.URI;
import java.util.Iterator;
import org.netkernel.container.IKernel;
import org.netkernel.module.standard.StandardSpace;
import org.netkernel.layer0.boot.BootUtils;
import org.netkernel.layer0.representation.IHDSNode;
import org.netkernel.layer0.representation.impl.HDSBuilder;
import org.netkernel.layer0.urii.SimpleIdentifierImpl;
import org.netkernel.urii.ISpaceWithIdentity;
import org.netkernel.urii.impl.Version;

public class SpaceStaticResourceListAccessor extends StandardAccessorImpl {
  public SpaceStaticResourceListAccessor() {
    this.declareThreadSafe();
    this.declareArgument(new SourcedArgumentMetaImpl("space",null,null,new Class[] {String.class}));
    this.declareArgument(new SourcedArgumentMetaImpl("version",null,null,new Class[] {String.class}));
  }

  private IHDSNode listDirectory(INKFRequestContext aContext, File aDirectoryFile) {
    return listDirectory(aContext, aDirectoryFile, "res:/");
  }

  private IHDSNode listDirectory(INKFRequestContext aContext, File aDirectoryFile, String aResourcePath) {
    HDSBuilder lDirectoryBuilder = new HDSBuilder();
    File[] lDirectoryFiles       = aDirectoryFile.listFiles();

    lDirectoryBuilder.pushNode("resources");
    for (int i=0; i < lDirectoryFiles.length; i++) {
      if (lDirectoryFiles[i].isDirectory()) {
        String lResourcePath = aResourcePath + lDirectoryFiles[i].getName() + "/";
        lDirectoryBuilder.importChildren(listDirectory(aContext,lDirectoryFiles[i],lResourcePath).getFirstNode("/resources"));
      }
      else {
        lDirectoryBuilder.addNode("resource", aResourcePath + lDirectoryFiles[i].getName());
      }
    }
    lDirectoryBuilder.popNode();
    return lDirectoryBuilder.getRoot();
  }

  public void onSource(INKFRequestContext aContext) throws Exception {
    // One mandatory argument
    String aSpaceIdentifier = aContext.getThisRequest().getArgumentValue("space");
    
    // One optional argument
    Version aVersion = null;
    if (aContext.getThisRequest().argumentExists("version")) {
      aVersion = new Version(aContext.getThisRequest().getArgumentValue("version"));
    }
    
    // Processing
    IKernel lKernel = null;
    ISpaceWithIdentity lSpace = null;
    StandardSpace lSS = null;
    String lSource;
    
    lKernel = aContext.getKernelContext().getKernel();
    lSpace  = lKernel.getSpace(new SimpleIdentifierImpl(aSpaceIdentifier), aVersion, aVersion);
    
    if (lSpace == null) {
      throw new NKFException("Space is not found");
    }
    
    if (!(lSpace instanceof StandardSpace)) {
      throw new NKFException ("Space is not a standard module space");
    }
    
    lSS = (StandardSpace)lSpace;
    lSource = lSS.getOwningModule().getSource().toString();
    lSource = BootUtils.fixURIString(lSource);
    
    IHDSNode lModuleXML = null;
    IHDSNode lRootSpace = null;
    if (lSource.startsWith("file:")) {
      lModuleXML = aContext.source(lSource + "module.xml",IHDSNode.class);
    }
    
    INKFRequest subrequest = aContext.createRequest("active:xslt");
    subrequest.addArgumentByValue("operand", lModuleXML);
    subrequest.addArgument("operator", "res:/resources/stylesheets/module.xsl");
    subrequest.addArgumentByValue("spaceid", aSpaceIdentifier);
    subrequest.setRepresentationClass(IHDSNode.class);
    
    lRootSpace = (IHDSNode)aContext.issueRequest(subrequest);

    File lSourceFile = new File(URI.create(lSource));
    IHDSNode lSourceContent = listDirectory(aContext,lSourceFile);
    
    HDSBuilder lValidResources = new HDSBuilder();
    lValidResources.pushNode("resources");

    Iterator<IHDSNode> lIterator = lRootSpace.getNodes("/rootspace/fileset").iterator();
    while(lIterator.hasNext()) {
      IHDSNode lFileset = lIterator.next();
      String lGlob  = null;
      String lRegex = null;
      lGlob  = (String)lFileset.getFirstValue("glob");
      lRegex = (String)lFileset.getFirstValue("regex");
      if (lGlob != null) {
        System.out.println("fileset glob = " + lGlob);
      }
      if (lRegex != null) {
        Iterator<IHDSNode> lIterResource = lSourceContent.getNodes("/resources/resource").iterator();
        while (lIterResource.hasNext()) {
          IHDSNode lResource = lIterResource.next();
          String lResourceString = (String)lResource.getValue();
          if (lResourceString.matches(lRegex)) {
            lValidResources.addNode("resource", lResourceString);
          }
        }
      }
    }
    lValidResources.popNode();

    // One response
    INKFResponse response = null;
    // response = aContext.createResponseFrom(lRootSpace.getRoot());
    // response = aContext.createResponseFrom(lSourceContent.getRoot());
    response = aContext.createResponseFrom(lValidResources.getRoot());
  }
}

What happens here ?
1) Determine the module of the rootspace that is passed as an argument.
2) Extract the rootspace from the module.xml of that module.
3) Determine all possible file resources of the module.
4) Loop through the filesets defined in the rootspace and use those to filter the file resources so only the valid ones remain.

This is the stylesheet that is used to extract the rootspace from module.xml :

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:nk="http://netkernel.org" 
                version="1.0">
  <xsl:output method="xml" 
              indent="yes" 
              encoding="UTF-8" 
              omit-xml-declaration="yes"/>
  <xsl:param name="spaceid" nk:class="java.lang.String" />

  <xsl:template match="/module">
    <xsl:copy-of select="rootspace[@uri=$spaceid]"/>
  </xsl:template>

</xsl:stylesheet>

And ... done.

Or so it seems, you might have noticed I labeled this entry "part 1". There's some work to be done yet :
* Next to regex, a fileset can also be glob or grammar defined. All three should be handled (at the moment only regex is).
* A fileset definition can contain a rewrite. I want to get the real valid resources, so we need to provide a rewrite too.
* A module can be expanded or jarred. At the moment the accessor only works for expanded modules. I want jars too.

As you can see there's room for a "part 2" (and beyond ?). Watch this space ! There might be other requirements you are interested in. Let me know and I'll add them.

Since this was a lot of code real fast, my next blog entry will once again be a philosophical one. You've been warned !