// SampleResourceIndexer.java
// $Id: SampleResourceIndexer.java,v 1.22 2002/10/29 14:39:02 ylafon Exp $
// (c) COPYRIGHT MIT and INRIA, 1997.
// Please first read the full copyright statement in file COPYRIGHT.html
package org.w3c.tools.resources.indexer;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.NoSuchElementException;
import java.util.Vector;
import java.io.File;
import org.w3c.tools.resources.Attribute;
import org.w3c.tools.resources.AttributeHolder;
import org.w3c.tools.resources.AttributeRegistry;
import org.w3c.tools.resources.ContainerInterface;
import org.w3c.tools.resources.ContainerResource;
import org.w3c.tools.resources.DummyResourceReference;
import org.w3c.tools.resources.FramedResource;
import org.w3c.tools.resources.InvalidResourceException;
import org.w3c.tools.resources.MultipleLockException;
import org.w3c.tools.resources.RequestInterface;
import org.w3c.tools.resources.Resource;
import org.w3c.tools.resources.ResourceContext;
import org.w3c.tools.resources.ResourceFrame;
import org.w3c.tools.resources.ResourceReference;
import org.w3c.tools.resources.ServerInterface;
import org.w3c.tools.resources.StringArrayAttribute;
import org.w3c.tools.resources.StringAttribute;
class SampleIndexerEnumeration implements Enumeration {
private static final String list[] = {
"directories".intern(),
"extensions".intern()
};
int idx = 0;
public boolean hasMoreElements() {
return idx < list.length;
}
public Object nextElement() {
if ( idx >= list.length )
throw new NoSuchElementException("SampleResourceIndexer enum");
return list[idx++];
}
SampleIndexerEnumeration() {
this.idx = 0;
}
}
/**
* A container for directories and templates.
*/
public class SampleResourceIndexer extends Resource
implements ContainerInterface, ResourceIndexer
{
private static final boolean debug = false;
protected static final boolean extCaseSensitive = false;
protected static final String defname = "*default*";
private static final String harmfulNames[] = {
"aux".intern(),
"con".intern()
};
static public boolean isWinPlatform = (File.pathSeparatorChar == ';');
/**
* Attribute index - the super indexer, if any.
*/
protected static int ATTR_SUPER_INDEXER = -1;
/**
* Attribute index - the super indexer, if any.
*/
protected static int ATTR_NOT_INDEXED = -1;
static {
Attribute a = null;
Class c =
org.w3c.tools.resources.indexer.SampleResourceIndexer.class;
// Our super indexer:
a = new StringAttribute("super-indexer"
, null
, Attribute.EDITABLE);
ATTR_SUPER_INDEXER = AttributeRegistry.registerAttribute(c, a);
a = new StringArrayAttribute("not-indexed-names"
, null
, Attribute.EDITABLE);
ATTR_NOT_INDEXED = AttributeRegistry.registerAttribute(c, a);
}
protected ResourceReference directories = null;
protected ResourceReference extensions = null;
protected ResourceReference contentTypes = null;
protected synchronized ResourceReference getDirectories() {
if ( directories == null ) {
String diridxid = getIdentifier()+"-d";
directories = new DummyResourceReference(
new TemplateContainer(
new ResourceContext(getContext()),
diridxid+".db"));
}
return directories;
}
protected synchronized ResourceReference getExtensions() {
if ( extensions == null ) {
String extidxid = getIdentifier()+"-e";
extensions = new DummyResourceReference(
new TemplateContainer(
new ResourceContext(getContext()),
extidxid+".db"));
}
return extensions;
}
public long lastModified() {
return getLong(ATTR_LAST_MODIFIED, -1);
}
public String getSuperIndexer() {
return getString(ATTR_SUPER_INDEXER, null);
}
public Enumeration enumerateResourceIdentifiers(boolean all) {
return new SampleIndexerEnumeration();
}
public ResourceReference lookup(String name) {
if ( name.equals("directories") ) {
return getDirectories();
} else if ( name.equals("extensions") ) {
return getExtensions();
}
return null;
}
/**
* Delete this inexer
* @exception org.w3c.tools.resources.MultipleLockException if someone
* else has locked the indexer.
*/
public synchronized void delete()
throws MultipleLockException
{
// Remove the two stores we are responsible for:
DummyResourceReference rr = (DummyResourceReference) getExtensions();
try {
Resource r = rr.lock();
r.delete();
} catch (InvalidResourceException ex) {
} finally {
rr.invalidate();
rr.unlock();
}
rr = (DummyResourceReference) getDirectories();
try {
Resource r = rr.lock();
r.delete();
} catch (InvalidResourceException ex) {
} finally {
rr.invalidate();
rr.unlock();
}
super.delete();
}
public void delete(String name) {
throw new RuntimeException("static container");
}
public void registerResource(String name,
Resource resource,
Hashtable defs) {
throw new RuntimeException("static container");
}
/*
* Load an extension descriptor.
* @param ext The name of the extension.
* @return An instance of Extension, or <strong>null</strong>.
*/
public synchronized ResourceReference loadExtension (String name) {
ResourceReference rr = getExtensions();
ResourceReference ext = null;
try {
TemplateContainer exts = (TemplateContainer) rr.lock();
// try with exact and if it fails, try to with lower case
ext = exts.lookup(name);
if (ext == null && !extCaseSensitive)
return exts.lookup(name.toLowerCase());
return ext;
} catch (InvalidResourceException ex) {
String msg = ("[resource indexer]: extensions \""+
name+
"\" couldn't be restored ("+ex.getMessage()+")");
getContext().getServer().errlog(msg);
return null;
} finally {
rr.unlock();
}
}
/**
* Return the class (if any) that our store defines for given extension.
* @param ext The extension we want a class for.
* @return A Class instance, or <strong>null</strong>.
*/
protected ResourceReference getTemplateFor(String ext) {
ResourceReference rr = loadExtension(ext) ;
if (rr != null) {
try {
Resource template = rr.lock();
if (template != null) {
Resource check = new Resource();
if (template.getClass() == check.getClass())
return null;
else return rr;
}
return null;
} catch (InvalidResourceException ex) {
return null;
} finally {
rr.unlock();
}
}
return null;
}
/**
* Merge the attributes this extension defines, with the provided ones.
* @param attrs The attributes we want to fill with default values.
* @param ext The extension name.
* @param into The already built set of default values.
* @return A Hashtable, containing the augmented set of default attribute
* values.
*/
protected Hashtable mergeDefaultAttributes(Resource template,
String ext,
Hashtable into) {
Attribute attrs[] = template.getAttributes();
ResourceReference rr = loadExtension(ext) ;
if (rr != null) {
try {
Resource e = rr.lock() ;
if ( e != null ) {
for (int i = 0 ; i < attrs.length ; i++) {
if ( ! template.definesAttribute(i) ) {
int idx = e.lookupAttribute(attrs[i].getName());
if ( idx >= 0 ) {
Object value = e.getValue(idx, null);
if ( value != null )
into.put(attrs[i].getName(), value) ;
}
}
}
}
return into ;
} catch (InvalidResourceException ex) {
return null;
} finally {
rr.unlock();
}
}
return null;
}
/**
* Merge the attributes this extension defines, with the provided ones.
* @param origFrame The original frame
* @param ext The extension name
* @param into The ResourceReference of the frame to be merged
*/
protected void mergeFrameAttributes(ResourceFrame origFrame, String ext,
ResourceReference frameref) {
int idx;
Object oldval, newval;
String atname;
try {
Resource frame = frameref.lock();
Attribute attrs[] = frame.getAttributes();
for (int i = 0 ; i < attrs.length ; i++) {
atname = attrs[i].getName();
try {
oldval = origFrame.getValue(atname, null);
if (oldval == null) { // not defined, try to merge
try {
origFrame.setValue(atname,
frame.getValue(atname, null));
} catch (Exception ex) {
// undefined value, will stay null
}
} else if (atname.equals("quality")) {
// small hack here, quality factor are merged
// should be removed by a quality per encoding
Double d = (Double)frame.getValue(atname, null);
if (d != null) {
d = new Double(d.doubleValue() *
((Double) oldval).doubleValue());
origFrame.setValue(atname, d);
}
}
} catch (Exception undefined) {
// attribute is NOT defined
}
}
} catch (InvalidResourceException ex) {
ex.printStackTrace();
} finally {
frameref.unlock();
}
}
/**
* Get this name's extensions.
* @param name The file name.
* @return An array of string, giving ach (parsed) extension, or
* <strong>null</strong> if none was found.
*/
private final static String noextension[] = { "*noextension*" } ;
protected String[] getFileExtensions(String name) {
Vector items = new Vector() ;
int dpos = name.indexOf ('.') ;
if ( dpos > 0 ) {
int pos = dpos+1 ;
while ( (dpos = name.indexOf ('.', pos)) != -1 ) {
// Skip empty extension:
if ( dpos == pos+1 ) {
pos++ ;
continue ;
}
// Else add new extension:
items.addElement (name.substring(pos, dpos)) ;
pos = dpos + 1;
}
if ( pos < name.length() )
items.addElement (name.substring(pos)) ;
String exts[] = new String[items.size()] ;
items.copyInto (exts) ;
return exts ;
} else {
// That file has no extensions, we'll use '.' as its extension
return noextension;
}
}
/**
* Create a default file resource for this file (that exists).
* @param directory The directory of the file.
* @param name The name of the file.
* @param defs A set of default attribute values.
* @return An instance of Resource, or <strong>null</strong> if
* we were unable to create it.
*/
protected Resource createFileResource(File directory,
RequestInterface req,
String name,
Hashtable defs)
{
ResourceReference rr = null;
FramedResource template = null;
Resource newres = null;
Class proto = null;
try {
proto = Class.forName("org.w3c.tools.resources.ProtocolFrame");
} catch (Exception ex) {
// fatal error!
return null;
}
// Check that at least one class is defined for all the extensions:
String exts[] = getFileExtensions(name) ;
if ( exts == null )
return null ;
for (int i = exts.length-1 ; i >= 0 ; i--) {
rr = getTemplateFor(exts[i]) ;
if ( rr != null )
break ;
}
if ( rr == null ) {
// Look for a default template:
if ((rr = loadExtension(defname)) == null)
return null ;
}
// Create the runtime-time default values for attributes.
if ( defs == null ) {
defs = new Hashtable(5) ;
}
String s_dir = "directory".intern();
String s_ide = "identifier".intern();
String s_fil = "filename".intern();
String s_con = "context".intern();
if ( defs.get(s_dir) == null ) {
defs.put(s_dir, directory) ;
}
if ( defs.get(s_ide) == null ) {
defs.put(s_ide, getIndexedFileName(name)) ;
} else {
defs.put(s_ide, getIndexedFileName((String)defs.get(s_ide))) ;
}
if ( defs.get(s_fil) == null) {
defs.put(s_fil, name) ;
}
if ( defs.get(s_con) == null ) {
defs.put(s_con, getContext());
}
try {
template = (FramedResource) rr.lock();
if (exts != null) {
// Merge with values defined by the extension:
for (int i = exts.length ; --i >= 0 ; )
mergeDefaultAttributes(template, exts[i], defs) ;
}
// Create, initialize and return the new resouce
try {
newres = (FramedResource) template.getClone(defs);
} catch (Exception ex) {
ex.printStackTrace() ;
return null ;
}
} catch (InvalidResourceException ex) {
ex.printStackTrace();
return null;
} finally {
rr.unlock();
}
// clone has been done, merge frames now
if (exts != null) {
ResourceFrame rf[] = newres.collectFrames(proto);
if (rf != null) {
for (int j=0; j < rf.length; j++) {
for (int i = exts.length-1 ; i >= 0 ; i--) {
rr = getTemplateFor(exts[i]) ;
if ( rr != null ) {
FramedResource fr = null;
try {
fr = (FramedResource) rr.lock();
ResourceReference trr = null;
trr = fr.getFrameReference(proto);
if (trr != null) {
mergeFrameAttributes(rf[j], exts[i], trr);
}
} catch (InvalidResourceException iex) {
iex.printStackTrace();
return null;
} finally {
rr.unlock();
}
}
}
}
}
}
return newres;
}
/**
* Load a given directory template from the store.
* @param name The name of the template to load.
* @return An instance of ResourceReference, or <strong>null</strong>.
*/
public synchronized ResourceReference loadDirectory(String name) {
ResourceReference rr = getDirectories();
try {
TemplateContainer dirs = (TemplateContainer) rr.lock();
return dirs.lookup(name);
} catch (InvalidResourceException ex) {
// Emit an error message, and remove it !
String msg = ("[resource indexer]: directory template \""+
name + "\" couldn't be restored. It has "+
"been removed.");
getContext().getServer().errlog(msg);
return null;
} finally {
rr.unlock();
}
}
/**
* Create a default container resource for this directory (that exists).
* @param directory The parent directory.
* @param req the request that triggered this creation
* @param name The name of its sub-directory to index.
* @param defaults A set of default atribute values.
* @return A Resource instance, or <strong>null</strong> if
* the indexer was unable to build a default resource for the directory.
*/
protected Resource createDirectoryResource(File directory,
RequestInterface req,
String name,
Hashtable defs)
{
// Lookup the directory path, for an existing template.
File dir = new File(directory, name) ;
Resource dirtempl = null;
ResourceReference rr = null;
rr = loadDirectory(name);
// If no template available, default to a raw DirectoryResource
if ((rr == null) && ((rr=loadDirectory(defname)) == null))
return null;
try {
dirtempl = rr.lock();
// Clone the appropriate template:
if ( defs == null ) {
defs = new Hashtable(3);
}
String s_dir = "directory".intern();
String s_ide = "identifier".intern();
if ( defs.get(s_dir) == null ) {
defs.put(s_dir, directory) ;
}
if ( defs.get(s_ide) == null ) {
defs.put(s_ide, getIndexedDirName(name)) ;
} else {
defs.put(s_ide, getIndexedDirName((String)defs.get(s_ide))) ;
}
//FIXME context ???
// if ( defs.get("context") == null )
// defs.put("context", getContext());
try {
return (Resource) dirtempl.getClone(defs);
} catch (Exception ex) {
ex.printStackTrace() ;
return null ;
}
} catch (InvalidResourceException ex) {
ex.printStackTrace();
return null;
} finally {
rr.unlock();
}
}
/**
* Try to create a virtual resource if the real (physical) resource
* is not there.
* @param directory The directory the file is in.
* @param req the request that triggered this creation
* @param name The name of the file.
* @param defs Any default attribute values that should be provided
* to the created resource at initialization time.
* @return A Resource instance, or <strong>null</strong> if the given
* file can't be truned into a resource given our configuration
* database.
*/
protected Resource createVirtualResource( File directory,
RequestInterface req,
String name,
Hashtable defs)
{
ResourceReference rr = null;
Resource dirtempl = null;
rr = loadDirectory(name);
if (rr != null) {
try {
dirtempl = rr.lock();
String classname = dirtempl.getClass().getName().intern();
String idr =
"org.w3c.jigsaw.resources.DirectoryResource".intern();
if (classname == idr) {
File file = new File(directory, name) ;
// check in this case that we will have a special
// configuration ONLY for a real directory
if (!file.exists()) {
return null;
}
if (!file.isDirectory()) {
return null;
}
}
String ifr =
"org.w3c.tools.resources.FileResource".intern();
if (classname == ifr) {
File file = new File(directory, name) ;
// check that we won't override a bad resource type
if (!file.exists()) {
return null;
}
if (file.isDirectory()) {
return null;
}
}
if ( defs == null ) {
defs = new Hashtable(4);
}
String s_dir = "directory".intern();
String s_ide = "identifier".intern();
String s_con = "context".intern();
if ( defs.get(s_dir) == null ) {
defs.put(s_dir, directory) ;
}
if ( defs.get(s_ide) == null ) {
defs.put(s_ide, name) ;
}
if ( defs.get(s_con) == null ) {
defs.put(s_con, getContext());
}
try {
return (Resource) dirtempl.getClone(defs);
} catch (Exception ex) {
ex.printStackTrace() ;
return null ;
}
} catch (InvalidResourceException ex) {
ex.printStackTrace();
return null;
} finally {
rr.unlock();
}
}
return null;
}
/**
* Try to create a resource for the given file.
* This method makes its best efforts to try to build a default
* resource out of a file.
* @param directory The directory the file is in.
* @param name The name of the file.
* @param defs Any default attribute values that should be provided
* to the created resource at initialization time.
* @return A Resource instance, or <strong>null</strong> if the given
* file can't be truned into a resource given our configuration
* database.
*/
public Resource createResource(ContainerResource container,
RequestInterface request,
File directory,
String name,
Hashtable defs)
{
if (isWinPlatform) {
for (int i=0; i < harmfulNames.length; i++) {
if (name.equalsIgnoreCase(harmfulNames[i])) {
return null;
}
}
}
// if it matches the Not-Indexed list, then exit
String[] removed = (String[]) getValue(ATTR_NOT_INDEXED, null);
if (removed != null) {
for (int i=0; i < removed.length; i++) {
if (name.equals(removed[i])) {
return null;
}
}
}
// Does this file exists ?
File file = new File(directory, name) ;
Resource result = null;
result = createVirtualResource(directory, request, name, defs);
if (result != null) {
return result;
}
if (!file.exists()) {
return null;
}
// Okay, dispatch on wether it is a file or a directory.
if ( file.isDirectory() ) {
result = createDirectoryResource(directory, request, name, defs) ;
} else if ( file.isFile() ) {
result = createFileResource(directory, request, name, defs) ;
} else {
// not a directory and not a real file, perhaps something not
// really wanted
return null;
}
if ( result != null )
return result;
// Try the super indexer if available:
String superIndexer = getSuperIndexer();
if ( superIndexer == null )
return null;
IndexerModule m = null;
m = (IndexerModule) getContext().getModule(IndexerModule.NAME);
ResourceReference rri = m.getIndexer(superIndexer);
if (rri == null)
return null;
try {
ResourceIndexer p = (ResourceIndexer)rri.lock();
return ((p != null)
? p.createResource(container, request, directory,
name, defs)
: null);
} catch (InvalidResourceException ex) {
return null;
} finally {
rri.unlock();
}
}
/**
* Get the name of the resource relative to the given filename.
* @param name The name of the file.
* @return a String, the resource name.
*/
public String getIndexedName(File directory, String name) {
if (isWinPlatform) {
for (int i=0; i < harmfulNames.length; i++) {
if (name.equalsIgnoreCase(harmfulNames[i])) {
return null;
}
}
}
File file = new File(directory, name);
if (! file.exists())
return null;
if (file.isDirectory())
return getIndexedDirName(name);
//make sure that we will index this file (or directory)
String exts[] = getFileExtensions(name) ;
ResourceReference rr = null;
if ( exts == null )
return null;
for (int i = exts.length-1 ; i >= 0 ; i--) {
rr = getTemplateFor(exts[i]) ;
if ( rr != null )
break ;
}
if (rr != null)
return getIndexedFileName(name);
//try the super indexer
String superIndexer = getSuperIndexer();
if ( superIndexer == null )
return null;
IndexerModule m = null;
m = (IndexerModule) getContext().getModule(IndexerModule.NAME);
ResourceReference rri = m.getIndexer(superIndexer);
if (rri == null)
return null;
try {
ResourceIndexer p = (ResourceIndexer)rri.lock();
return ((p != null)
? p.getIndexedName(directory, name)
: null);
} catch (InvalidResourceException ex) {
return null;
} finally {
rri.unlock();
}
}
protected String getIndexedFileName(String name) {
return name;
}
protected String getIndexedDirName(String name) {
return name;
}
// FIXME tests
public SampleResourceIndexer(ResourceContext ctxt) {
Hashtable init = new Hashtable(3);
String s_ide = "identifier".intern();
String s_con = "context".intern();
init.put(s_con, ctxt);
init.put(s_ide, "default");
initialize(init);
}
public SampleResourceIndexer() {
super();
}
}