001: /*=============================================================================
002: * Copyright Texas Instruments 2002. All Rights Reserved.
003: *
004: * This program is free software; you can redistribute it and/or modify
005: * it under the terms of the GNU General Public License as published by
006: * the Free Software Foundation; either version 2 of the License, or
007: * (at your option) any later version.
008: *
009: * This program is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU General Public License for more details.
013: *
014: * You should have received a copy of the GNU General Public License
015: * along with this program; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */
018:
019: package ti.chimera.registry;
020:
021: import ti.chimera.*;
022: import ti.exceptions.ProgrammingErrorException;
023:
024: import oscript.data.JavaBridge;
025: import oscript.data.Value;
026:
027: import java.util.*;
028:
029: /**
030: * The implementation of the "core" registry API.
031: *
032: * @author ;Rob Clark;a0873619;San Diego;;
033: * @version 0.1
034: */
035: public abstract class RegistryCore {
036: static final Object lock = new Object();
037:
038: /**
039: * link to main
040: */
041: protected Main main;
042:
043: /**
044: * The root node.
045: */
046: protected final Node root;
047:
048: /**
049: * Register script function transformers here, because we can't have
050: * static code in interfaces... kinda a pain, but what can ya do?
051: */
052: static {
053:
054: JavaBridge
055: .registerFunctionTransformer(new JavaBridge.FunctionTransformer(
056: NodeSubscriber.class) {
057: public Object transform(Value fxn) {
058: return new NodeSubscriber.ScriptFunctionNodeSubscriber(
059: fxn);
060: }
061: });
062:
063: JavaBridge
064: .registerFunctionTransformer(new JavaBridge.FunctionTransformer(
065: NodeCreationSubscriber.class) {
066: public Object transform(Value fxn) {
067: return new NodeCreationSubscriber.ScriptFunctionNodeCreationSubscriber(
068: fxn);
069: }
070: });
071:
072: JavaBridge
073: .registerFunctionTransformer(new JavaBridge.FunctionTransformer(
074: NodeDeletionSubscriber.class) {
075: public Object transform(Value fxn) {
076: return new NodeDeletionSubscriber.ScriptFunctionNodeDeletionSubscriber(
077: fxn);
078: }
079: });
080:
081: }
082:
083: /*=======================================================================*/
084: /**
085: * Class Constructor.
086: */
087: protected RegistryCore(Main main) {
088: this .main = main;
089: root = new DirectoryNode();
090: root.link("/");
091: }
092:
093: /*
094: * Core API
095: */
096:
097: /*=======================================================================*/
098: /**
099: * Link in a new <code>node</code> to the specified <code>path</code>.
100: * This will make the node a part of the registry, so it will be
101: * accessible by other parts of the system. It is permitted for the
102: * same node to be <code>link()</code> in to multiple locations in
103: * the tree. The <code>link()</code> operation will cause the parent
104: * directory to be created, if needed.
105: *
106: * @param node the node to link in
107: * @param path the path to link the node in to
108: * @throws RegistryException if a node is already linked in at the
109: * specified path
110: * @see #unlink
111: * @see #mkdir
112: */
113: public void link(Node node, String path) throws RegistryException {
114: synchronized (lock) {
115: path = normalize(path);
116:
117: // if( node.getValue() instanceof DirectoryTable )
118: // {
119: // XXX detect loops, throw new RegistryException("attempted incest");
120: // }
121:
122: Node dir = mkdirImpl(dirname(path));
123: node.link(path);
124: dir.setValue(((DirectoryTable) (dir.getValue())).add(node,
125: basename(path)));
126: }
127: }
128:
129: /*=======================================================================*/
130: /**
131: * Unlink a node. This will remove a link to the node from the tree,
132: * and if there are no more references to the node could result in the
133: * node being garbage collected.
134: *
135: * @param path the path to the node to unlink
136: * @throws RegistryException if there is no node linked in at the
137: * specified path, or if this is the last link to a directory
138: * node that still has children
139: * @see #link
140: * @see #mkdir
141: */
142: public void unlink(String path) throws RegistryException {
143: unlink(path, false);
144: }
145:
146: /*=======================================================================*/
147: /**
148: * Unlink a node. This will remove a link to the node from the tree,
149: * and if there are no more references to the node could result in the
150: * node being garbage collected.
151: *
152: * @param path the path to the node to unlink
153: * @param recursive if <code>true</code>, and the specified node is a
154: * directory node, then child nodes are recursively unlinked
155: * @throws RegistryException if there is no node linked in at the
156: * specified path, or if this is the last link to a directory
157: * node that still has children and <code>recursive</code> is
158: * <code>false</code>
159: * @see #link
160: * @see #mkdir
161: */
162: public void unlink(String path, boolean recursive)
163: throws RegistryException {
164: synchronized (lock) {
165: path = normalize(path);
166:
167: if (recursive) {
168: recursiveUnlink(path);
169: } else {
170: Node dir = resolveImpl(dirname(path), true);
171: Node node = resolveImpl(path, true);
172:
173: // NOTE: should unlink() after dir.setValue(), in-case setValue()
174: // throws an exception
175: dir.setValue(((DirectoryTable) (dir.getValue()))
176: .remove(basename(path),
177: node.getPathCount() == 1));
178: node.unlink(path);
179: }
180: }
181: }
182:
183: private void recursiveUnlink(String path) throws RegistryException {
184: Node node = resolveImpl(path, true);
185:
186: if (node.getValue() instanceof DirectoryTable) {
187: for (Iterator itr = ((DirectoryTable) (node.getValue()))
188: .getChildNames(); itr.hasNext();)
189: recursiveUnlink(path + "/" + itr.next());
190: }
191:
192: unlink(path, false);
193: }
194:
195: /*=======================================================================*/
196: /**
197: * Find the node at the specified <code>path</code>.
198: *
199: * @param path the path to the node to resolve
200: * @throws RegistryException if there is no node linked in at the
201: * specified path
202: */
203: public Node resolve(String path) throws RegistryException {
204: synchronized (lock) {
205: return resolveImpl(normalize(path), true);
206: }
207: }
208:
209: private Node resolveImpl(String path, boolean exception) // call with lock & normalized path
210: throws RegistryException {
211: if (path.equals("/"))
212: return root;
213:
214: Node dir = resolveImpl(dirname(path), exception);
215: Node child = null;
216:
217: if (dir != null) {
218: if (!(dir.getValue() instanceof DirectoryTable))
219: throw new RegistryException("not a directory: "
220: + dirname(path));
221: DirectoryTable tbl = (DirectoryTable) (dir.getValue());
222: child = tbl.get(basename(path));
223: }
224:
225: if (exception && (child == null))
226: throw new RegistryException("no such child: "
227: + basename(path));
228:
229: return child;
230: }
231:
232: /*=======================================================================*/
233: /**
234: * Determine if the node at the specified <code>path</code> exists.
235: *
236: * @param path the path to the node
237: * @return <code>true</code> if node exists, else <code>false</code>
238: */
239: public boolean exists(String path) {
240: synchronized (lock) {
241: try {
242: return resolveImpl(normalize(path), false) != null;
243: } catch (RegistryException e) {
244: throw new ProgrammingErrorException(
245: "shouldn't happen: " + e.getMessage());
246: }
247: }
248: }
249:
250: /*=======================================================================*/
251: /**
252: * Create a directory node, if one does not already exist. Recursively
253: * creates parent directories if needed.
254: *
255: * @param path the path to the node to unlink
256: * @return the created node
257: */
258: public Node mkdir(String path) {
259: synchronized (lock) {
260: return mkdirImpl(normalize(path));
261: }
262: }
263:
264: private Node mkdirImpl(String path) // call with lock & normalized path
265: {
266: if (path.equals("/"))
267: return root;
268:
269: Node dir = mkdirImpl(dirname(path));
270: DirectoryTable tbl = (DirectoryTable) (dir.getValue());
271: Node child = tbl.get(basename(path));
272:
273: if (child == null) {
274: child = new DirectoryNode();
275: try {
276: link(child, path);
277: } catch (RegistryException e) {
278: // shouldn't happen unless someone adds to dir without holding registry lock
279: throw new ProgrammingErrorException(
280: "concurrent modification?");
281: }
282: }
283:
284: return child;
285: }
286:
287: /*=======================================================================*/
288: /**
289: * A directory-node. Directories are nodes of this type. This is just a
290: * convenience so we don't have to specify args to the node constructor
291: * to create a node whose contents is a {@link DirectoryTable}.
292: */
293: public static class DirectoryNode extends Node {
294: DirectoryNode() {
295: super (new DirectoryTable(), new TypeNodeContract(
296: DirectoryTable.class), "");
297: }
298: }
299:
300: /*
301: * Utility API:
302: *
303: * NOTE: these utility methods were stolen from oscript.fs.AbstractFileSystem
304: * because (a) it was already implemented and (b) already tested.
305: */
306:
307: /**
308: * Normalize the path. This involves ensuring the path is
309: * fully qualified (begins with "/"), and gets rid of any
310: * extra "." and "/".
311: */
312: public static String normalize(String path) {
313: int idx;
314:
315: // replace '\' with '/':
316: while ((idx = path.indexOf('\\')) != -1)
317: path = path.substring(0, idx) + '/'
318: + path.substring(idx + 1);
319:
320: // add leading '/':
321: if (path.length() == 0)
322: path = "/";
323: else if (path.charAt(0) != '/')
324: path = '/' + path;
325:
326: // makes the next few steps simpler:
327: path += '/';
328:
329: // handle '/.':
330: while ((idx = path.indexOf("/./")) != -1)
331: path = path.substring(0, idx) + path.substring(idx + 2);
332:
333: // handle '/..':
334: while ((idx = path.indexOf("/../")) != -1)
335: path = path.substring(0, path.lastIndexOf('/', idx - 1))
336: + path.substring(idx + 3);
337:
338: // remove trailing '/':
339: while ((path.length() > 0)
340: && (path.charAt(path.length() - 1) == '/'))
341: path = path.substring(0, path.length() - 1);
342:
343: // remove double '//':
344: while ((idx = path.indexOf("//")) != -1)
345: path = path.substring(0, idx) + path.substring(idx + 1);
346:
347: // deal with case where "/" had it's trailing (and only) "/" stripped:
348: if (path.length() == 0)
349: path = "/";
350:
351: return path;
352: }
353:
354: /**
355: * Given a normalized path, return dirname. Ie. everything but the last
356: * component in the path.
357: */
358: public static String dirname(String path) {
359: int idx = path.lastIndexOf('/');
360: if (idx > 0)
361: return path.substring(0, idx);
362: else
363: return "/";
364: }
365:
366: /**
367: * Given a normalized path, return basename. Ie. the last component of the
368: * path.
369: */
370: public static String basename(String path) {
371: int idx = path.lastIndexOf('/');
372: if (idx == -1)
373: return path;
374: else
375: return path.substring(idx + 1);
376: }
377:
378: }
379:
380: /*
381: * Local Variables:
382: * tab-width: 2
383: * indent-tabs-mode: nil
384: * mode: java
385: * c-indentation-style: java
386: * c-basic-offset: 2
387: * eval: (c-set-offset 'substatement-open '0)
388: * eval: (c-set-offset 'case-label '+)
389: * eval: (c-set-offset 'inclass '+)
390: * eval: (c-set-offset 'inline-open '0)
391: * End:
392: */
|