001: // ContainerResource.java
002: // $Id: ContainerResource.java,v 1.21 2004/02/10 13:34:44 ylafon Exp $
003: // (c) COPYRIGHT MIT and INRIA, 1996.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005:
006: package org.w3c.tools.resources;
007:
008: import java.util.Date;
009: import java.util.Enumeration;
010: import java.util.Hashtable;
011:
012: import org.w3c.tools.resources.event.Events;
013: import org.w3c.tools.resources.event.StructureChangedEvent;
014: import org.w3c.tools.resources.store.ResourceStoreManager;
015:
016: /**
017: * This resource manage children resources.
018: */
019: public class ContainerResource extends AbstractContainer {
020:
021: public static boolean debug = false;
022:
023: /**
024: * Attribute index - The index of the resource key.
025: */
026: protected static int ATTR_KEY = -1;
027:
028: static {
029: Attribute a = null;
030: Class cls = null;
031: // Get a pointer to our own class:
032: try {
033: cls = Class
034: .forName("org.w3c.tools.resources.ContainerResource");
035: } catch (Exception ex) {
036: ex.printStackTrace();
037: System.exit(1);
038: }
039: // The identifier attribute:
040: a = new IntegerAttribute("key", null, Attribute.COMPUTED);
041: ATTR_KEY = AttributeRegistry.registerAttribute(cls, a);
042: }
043:
044: public Object getClone(Object values[]) {
045: values[ATTR_KEY] = null;
046: return super .getClone(values);
047: }
048:
049: /**
050: * Get the container Key. This key must be unique and unchanged
051: * during the container life.
052: * @return a String instance.
053: */
054: public Integer getKey() {
055: Integer key = (Integer) unsafeGetValue(ATTR_KEY, null);
056: if (key == null) {
057: key = new Integer(getIdentifier().hashCode()
058: ^ (new Date().hashCode()));
059: if (debug) {
060: System.out.println("*** new key is: " + key);
061: }
062: ResourceStoreManager rsm = getServer()
063: .getResourceStoreManager();
064: while (!rsm.checkKey(key)) {
065: // key = new Integer (key.intValue() ^ (int) Math.random());
066: key = new Integer(
067: (int) ((1.9 * (Math.random() - 0.5)) * Integer.MAX_VALUE));
068:
069: if (debug) {
070: System.out.println("*** updated key is: " + key);
071: }
072: }
073: setValue(ATTR_KEY, key);
074: }
075: return key;
076: }
077:
078: protected SpaceEntry getSpaceEntry() {
079: ResourceReference rr = getParent();
080: if (rr == null) //I'm root or external!!
081: return new SpaceEntryImpl(this );
082: try {
083: //FIXME sure that is a containerResource?
084: ContainerResource cont = (ContainerResource) rr.lock();
085: return new SpaceEntryImpl(cont);
086: } catch (InvalidResourceException ex) {
087: System.out.println(ex.getMessage());
088: ex.printStackTrace();
089: return null;
090: } finally {
091: rr.unlock();
092: }
093: }
094:
095: /**
096: * Get the SpaceEntry of our children resources.
097: * @return A SpaceEntry instance.
098: */
099: protected SpaceEntry getChildrenSpaceEntry() {
100: return new SpaceEntryImpl(this );
101: }
102:
103: /**
104: * This handles the <code>RESOURCE_MODIFIED</code> kind of events.
105: * @param evt The StructureChangeEvent.
106: */
107: public void resourceModified(StructureChangedEvent evt) {
108: if (!isUnloaded())
109: super .resourceModified(evt);
110: }
111:
112: /**
113: * A new resource has been created in some space.
114: * This handles the <code>RESOURCE_CREATED</code> kind of events.
115: * @param evt The event describing the change.
116: */
117: public void resourceCreated(StructureChangedEvent evt) {
118: if (!isUnloaded())
119: super .resourceCreated(evt);
120: }
121:
122: /**
123: * A resource is about to be removed
124: * This handles the <code>RESOURCE_REMOVED</code> kind of events.
125: * @param evt The event describing the change.
126: */
127: public void resourceRemoved(StructureChangedEvent evt) {
128: if (!isUnloaded())
129: super .resourceRemoved(evt);
130: }
131:
132: /**
133: * Update default child attributes.
134: * A parent can often pass default attribute values to its children,
135: * such as a pointer to itself (the <em>parent</em> attribute).
136: * <p>This is the method to overide when you want your container
137: * to provide these kinds of attributes. By default this method will set
138: * the following attributes:
139: * <dl><dt>name<dd>The name of the child (it's identifier) -
140: * String instance.
141: * <dt>parent<dd>The parent of the child (ie ourself here) -
142: * a ContainerResource instance.
143: * <dt>url<dd>If a <em>identifier</em> attribute is defined, that
144: * attribute is set to the full URL path of the children.
145: * </dl>
146: */
147:
148: protected ResourceContext updateDefaultChildAttributes(
149: Hashtable attrs) {
150: ResourceContext context = super
151: .updateDefaultChildAttributes(attrs);
152: if (context == null) {
153: context = new ResourceContext(getContext());
154: attrs.put(co, context);
155: }
156: String name = (String) attrs.get(id);
157: if (name != null) {
158: StringBuffer sb = new StringBuffer(128);
159: sb.append(getURLPath());
160: sb.append(java.net.URLEncoder.encode(name));
161: attrs.put(ur, sb.toString());
162: }
163: return context;
164: }
165:
166: /**
167: * Enumerate children resource identifiers.
168: * @param all Should all resources be enumerated ? Resources are often
169: * created on demand only, this flag allows the caller to tell the
170: * container about wether it is interested only in already created
171: * resources, or in all resources (even the one that have not yet been
172: * created).
173: * @return An String enumeration, one element per child.
174: */
175:
176: public synchronized Enumeration enumerateResourceIdentifiers(
177: boolean all) {
178: ResourceSpace space = getSpace();
179: acquireChildren();
180: return space
181: .enumerateResourceIdentifiers(getChildrenSpaceEntry());
182: }
183:
184: /**
185: * Create a default child resource in that container.
186: * This method is called by the editor to add a default resource
187: * in the container under the given name. The meaning of <em>default</em>
188: * is left up to the container here.
189: * @param name The identifier for the new resource.
190: */
191: public ResourceReference createDefaultResource(String name) {
192: return null;
193: }
194:
195: /**
196: * Get the number of matching character (case sensitive).
197: * ex getMatchingCharsCount("index.html", "Index.html") = 10.
198: * @param s1 the first string.
199: * @param s2 the second string.
200: * @return -1 if s1 and s2 are not equals (ignoring case),
201: * otherwise the number of matching character (case sensitive).
202: */
203: protected int getMatchingCharsCount(String s1, String s2) {
204: int len = -1;
205: int matching = 0;
206: if (s1 == null || s2 == null
207: || ((len = s1.length()) != s2.length()))
208: return -1;
209:
210: for (int i = 0; i < len; i++) {
211: char c1 = s1.charAt(i);
212: char c2 = s2.charAt(i);
213: if (c1 == c2) {
214: matching++;
215: continue;
216: }
217: c1 = Character.toUpperCase(c1);
218: c2 = Character.toUpperCase(c2);
219: if (c1 != c2)
220: return -1;
221: }
222: return matching;
223: }
224:
225: /**
226: * Lookup a children in the container.
227: * @param name The name of the children to lookup.
228: */
229: public ResourceReference lookup(String name) {
230: acquireChildren();
231: SpaceEntry sp = getChildrenSpaceEntry();
232: ResourceSpace space = getSpace();
233: ResourceReference rr = internalLookup(name, sp, space);
234:
235: if ((rr == null) && (!getServer().checkFileSystemSensitivity())) {
236: Enumeration children = space
237: .enumerateResourceIdentifiers(sp);
238: //look for possible matching identifier
239: int max = -1;
240: String realname = null;
241: while (children.hasMoreElements()) {
242: String child = (String) children.nextElement();
243: int matching = getMatchingCharsCount(name, child);
244: if (matching > max) {
245: max = matching;
246: realname = child;
247: }
248: }
249: if (realname != null)
250: rr = internalLookup(realname, sp, space);
251: }
252: return rr;
253: }
254:
255: protected ResourceReference internalLookup(String name,
256: SpaceEntry sp, ResourceSpace space) {
257: ResourceReference rr = space.lookupResource(sp, name);
258: if (rr != null)
259: return rr;
260: synchronized (this ) {
261: rr = space.lookupResource(sp, name);
262: if (rr != null)
263: return rr;
264: Hashtable defs = new Hashtable(5);
265: defs.put(id, name);
266: ResourceContext context = updateDefaultChildAttributes(defs);
267: rr = space.loadResource(sp, name, defs);
268: if (rr != null) {
269: context.setResourceReference(rr);
270: try {
271: Resource resource = rr.lock();
272: if (resource instanceof FramedResource) {
273: FramedResource fres = (FramedResource) resource;
274: fres.addStructureChangedListener(this );
275: // send event
276: }
277: } catch (InvalidResourceException ex) {
278: // nothing here
279: } finally {
280: rr.unlock();
281: }
282: }
283: }
284: return rr;
285: }
286:
287: /**
288: * Lookup the next component of this lookup state in here.
289: * @param ls The current lookup state.
290: * @param lr The lookup result under construction.
291: * @exception ProtocolException If an error occurs.
292: * @return A boolean, <strong>true</strong> if lookup has completed,
293: * <strong>false</strong> if it should be continued by the caller.
294: */
295: public boolean lookup(LookupState ls, LookupResult lr)
296: throws ProtocolException {
297: // Give a chance to our super-class to run its own lookup scheme:
298: if (super .lookup(ls, lr))
299: return true;
300: // Perform our own lookup on the next component:
301: String name = ls.getNextComponent();
302: ResourceReference rr = null;
303: rr = lookup(name);
304: if (rr == null) {
305: lr.setTarget(null);
306: return false;
307: }
308: try {
309: lr.setTarget(rr);
310: FramedResource resource = (FramedResource) rr.lock();
311: return (resource != null) ? resource.lookup(ls, lr) : false;
312: } catch (InvalidResourceException ex) {
313: return false;
314: } finally {
315: rr.unlock();
316: }
317: }
318:
319: /**
320: * Remove a child resource from that container.
321: * @param name The name of the child to remove.
322: * @exception MultipleLockException If somone else has locked the
323: * resource.
324: */
325:
326: public void delete(String name) throws MultipleLockException {
327: ResourceReference rr = null;
328: rr = lookup(name);
329:
330: if (rr != null) {
331: try {
332: synchronized (rr) {
333: Resource resource = rr.lock();
334: if (resource instanceof FramedResource)
335: ((FramedResource) resource)
336: .removeStructureChangedListener(this );
337: resource.delete();
338: }
339: } catch (InvalidResourceException ex) {
340: // FIXME ??
341: } finally {
342: rr.unlock();
343: }
344: }
345: }
346:
347: /**
348: * Delete that container and its children if children is true
349: * @exception MultipleLockException If somone else has locked one
350: * of the resource child.
351: */
352: public synchronized void replace(DirectoryResource newdir)
353: throws MultipleLockException {
354: Enumeration e = enumerateResourceIdentifiers();
355: ResourceReference rr = null;
356: Resource resource = null;
357: while (e.hasMoreElements()) {
358: rr = lookup((String) e.nextElement());
359: if (rr != null) {
360: try {
361: resource = rr.lock();
362: ResourceContext ctxt = new ResourceContext(newdir
363: .getContext());
364: resource.setContext(ctxt, true);
365: if (resource instanceof FramedResource) {
366: ((FramedResource) resource)
367: .removeStructureChangedListener(this );
368: ((FramedResource) resource)
369: .addStructureChangedListener(newdir);
370: }
371: } catch (InvalidResourceException ex) {
372: // do nothing , continue
373: } finally {
374: rr.unlock();
375: }
376: }
377: }
378: super .delete();
379: }
380:
381: /**
382: * Delete that resource container.
383: * @exception MultipleLockException If somone else has locked the
384: * resource.
385: */
386: public synchronized void delete() throws MultipleLockException {
387: disableEvent();
388: ResourceSpace space = getSpace();
389: //delete our children
390: acquireChildren();
391: deleteChildren();
392: disableEvent();
393: SpaceEntry sentry = getChildrenSpaceEntry();
394: //delete myself
395: super .delete();
396: space.deleteChildren(sentry);
397: }
398:
399: protected synchronized void deleteChildren()
400: throws MultipleLockException {
401: disableEvent();
402: acquireChildren();
403: Enumeration e = enumerateResourceIdentifiers();
404: while (e.hasMoreElements())
405: delete((String) e.nextElement());
406: enableEvent();
407: }
408:
409: /**
410: * This resource is being unloaded.
411: * The resource is being unloaded from memory, perform any additional
412: * cleanup required.
413: */
414: public void notifyUnload() {
415: super .notifyUnload();
416: // anything else?
417: }
418:
419: protected boolean acquired = false;
420:
421: /**
422: * Acquire our children. Must be called before all child manipulation.
423: */
424: protected synchronized void acquireChildren() {
425: if (!acquired) {
426: ResourceSpace space = getSpace();
427: space.acquireChildren(getChildrenSpaceEntry());
428: acquired = true;
429: }
430: }
431:
432: /**
433: * Add an initialized resource into this store container instance.
434: * @param resource The resource to be added to the store.
435: */
436:
437: protected synchronized ResourceReference addResource(
438: Resource resource, Hashtable defs) {
439: acquireChildren();
440: ResourceReference rr = getSpace().addResource(
441: getChildrenSpaceEntry(), resource, defs);
442: resource.getContext().setResourceReference(rr);
443: if (resource instanceof FramedResource) {
444: FramedResource fres = (FramedResource) resource;
445: fres.addStructureChangedListener(this );
446: }
447: markModified();
448: postStructureChangedEvent(rr, Events.RESOURCE_CREATED);
449: return rr;
450: }
451:
452: /**
453: * Initialize and register the given resource within that container.
454: * @param name The identifier for the resource.
455: * @param resource An unitialized resource instance.
456: * @param defs A default set of init attribute values (may be
457: * <strong>null</strong>).
458: * @exception InvalidResourceException If an error occurs during
459: * the registration.
460: */
461:
462: public void registerResource(String name, Resource resource,
463: Hashtable defs) throws InvalidResourceException {
464: acquireChildren();
465: // Create a default set of attributes:
466: if (defs == null)
467: defs = new Hashtable(4);
468: defs.put(id, name);
469: ResourceContext context = updateDefaultChildAttributes(defs);
470: if (context != null) {
471: resource.initialize(defs);
472: ResourceReference rr;
473: rr = getSpace().addResource(getChildrenSpaceEntry(),
474: resource, defs);
475: context.setResourceReference(rr);
476: if (resource instanceof FramedResource) {
477: FramedResource fres = (FramedResource) resource;
478: fres.addStructureChangedListener(this );
479: // send event
480: }
481: markModified();
482: postStructureChangedEvent(rr, Events.RESOURCE_CREATED);
483: } else {
484: throw new InvalidResourceException(getIdentifier(), name,
485: "unable to get context");
486: }
487: }
488:
489: /**
490: * Initialize ourself.
491: * As we are a container resource that really contains something, we make
492: * sure our URL ends properly with a slash.
493: * @param values Our default attribute values.
494: */
495: public void initialize(Object values[]) {
496: super .initialize(values);
497: disableEvent();
498: // If my URL doesn't end with a slah, correct it:
499: String url = getURLPath();
500: if ((url != null) && !url.endsWith("/"))
501: setValue(ATTR_URL, url + "/");
502: acquired = false;
503: enableEvent();
504: }
505: }
|