Source Code Cross Referenced for Ensemble.java in  » Scripting » jacl » itcl » lang » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Scripting » jacl » itcl.lang 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * ------------------------------------------------------------------------
0003:         *      PACKAGE:  [incr Tcl]
0004:         *  DESCRIPTION:  Object-Oriented Extensions to Tcl
0005:         *
0006:         *  [incr Tcl] provides object-oriented extensions to Tcl, much as
0007:         *  C++ provides object-oriented extensions to C.  It provides a means
0008:         *  of encapsulating related procedures together with their shared data
0009:         *  in a local namespace that is hidden from the outside world.  It
0010:         *  promotes code re-use through inheritance.  More than anything else,
0011:         *  it encourages better organization of Tcl applications through the
0012:         *  object-oriented paradigm, leading to code that is easier to
0013:         *  understand and maintain.
0014:         *
0015:         *  This part handles ensembles, which support compound commands in Tcl.
0016:         *  The usual "info" command is an ensemble with parts like "info body"
0017:         *  and "info globals".  Extension developers can extend commands like
0018:         *  "info" by adding their own parts to the ensemble.
0019:         *
0020:         * ========================================================================
0021:         *  AUTHOR:  Michael J. McLennan
0022:         *           Bell Labs Innovations for Lucent Technologies
0023:         *           mmclennan@lucent.com
0024:         *           http://www.tcltk.com/itcl
0025:         *
0026:         *     RCS:  $Id: Ensemble.java,v 1.3 2006/01/26 19:49:18 mdejong Exp $
0027:         * ========================================================================
0028:         *           Copyright (c) 1993-1998  Lucent Technologies, Inc.
0029:         * ------------------------------------------------------------------------
0030:         * See the file "license.itcl" for information on usage and redistribution
0031:         * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
0032:         */
0033:
0034:        package itcl.lang;
0035:
0036:        import tcl.lang.*;
0037:
0038:        //  Data used to represent an ensemble:
0039:
0040:        class EnsemblePart {
0041:            String name; // name of this part
0042:            int minChars; // chars needed to uniquely identify part
0043:            Command cmd; // command handling this part
0044:            WrappedCommand wcmd; // wrapped for command
0045:            String usage; // usage string describing syntax
0046:            Ensemble ensemble; // ensemble containing this part
0047:        }
0048:
0049:        //  Data shared by ensemble access commands and ensemble parser:
0050:
0051:        class EnsembleParser implements  AssocData {
0052:            Interp master; // master interp containing ensembles
0053:            Interp parser; // slave interp for parsing
0054:            Ensemble ensData; // add parts to this ensemble
0055:
0056:            public void disposeAssocData(Interp interp) {
0057:                Ensemble.DeleteEnsParser(this , this .master);
0058:            }
0059:        }
0060:
0061:        //  This class defines a Tcl object type that takes the
0062:        //  place of a part name during ensemble invocations.  When an
0063:        //  error occurs and the caller tries to print objv[0], it will
0064:        //  get a string that contains a complete path to the ensemble
0065:        //  part.
0066:
0067:        class ItclEnsInvoc implements  InternalRep /*, CommandWithDispose*/{
0068:            EnsemblePart ensPart;
0069:            TclObject chainObj;
0070:
0071:            // Implement InternalRep interface
0072:            // Note: SetEnsInvocFromAny is not used
0073:
0074:            public InternalRep duplicate() {
0075:                return Ensemble.DupEnsInvocInternalRep(this );
0076:            }
0077:
0078:            public void dispose() {
0079:                Ensemble.FreeEnsInvocInternalRep(this );
0080:            }
0081:
0082:            public String toString() {
0083:                return Ensemble.UpdateStringOfEnsInvoc(this );
0084:            }
0085:
0086:            public static TclObject newInstance() {
0087:                return new TclObject(new ItclEnsInvoc());
0088:            }
0089:
0090:            /*
0091:             // Implement CommandWithDispose interface
0092:
0093:             public void cmdProc(Interp interp, TclObject argv[])
0094:             throws TclException {}
0095:
0096:             public void disposeCmd() {}
0097:             */
0098:        }
0099:
0100:        //  Data/Methods in Ensemble class
0101:
0102:        class Ensemble {
0103:            Interp interp; // interpreter containing this ensemble
0104:            EnsemblePart[] parts; // list of parts in this ensemble
0105:            int numParts; // number of parts in part list
0106:            int maxParts; // current size of parts list
0107:            WrappedCommand wcmd; // command representing this ensemble
0108:            EnsemblePart parent; // parent part for sub-ensembles
0109:
0110:            // NULL => toplevel ensemble
0111:
0112:            // Helper function used throughout this module to parse
0113:            // and Ensemble name into an array of String objects.
0114:
0115:            static String[] SplitEnsemble(Interp interp, String ensName)
0116:                    throws TclException {
0117:                TclObject list = TclString.newInstance(ensName);
0118:                TclObject[] objArgv;
0119:                String[] strArgv;
0120:
0121:                objArgv = TclList.getElements(interp, list);
0122:                strArgv = new String[objArgv.length];
0123:
0124:                for (int i = 0; i < objArgv.length; i++) {
0125:                    strArgv[i] = objArgv[i].toString();
0126:                }
0127:
0128:                return strArgv;
0129:            }
0130:
0131:            // Helper function that merges an ensemble array back
0132:            // into a valid Tcl list contained in a String.
0133:
0134:            static String MergeEnsemble(Interp interp, String[] nameArgv,
0135:                    int nameArgc) throws TclException {
0136:                TclObject list = TclList.newInstance();
0137:
0138:                for (int i = 0; i < nameArgc; i++) {
0139:                    TclList.append(interp, list, TclString
0140:                            .newInstance(nameArgv[i]));
0141:                }
0142:
0143:                return list.toString();
0144:            }
0145:
0146:            /*
0147:             *----------------------------------------------------------------------
0148:             *
0149:             * Itcl_EnsembleInit -> Ensemble.EnsembleInit
0150:             *
0151:             *      Called when any interpreter is created to make sure that
0152:             *      things are properly set up for ensembles.
0153:             *
0154:             * Results:
0155:             *      None.
0156:             *
0157:             * Side effects:
0158:             *      On the first call, the "ensemble" object type is registered
0159:             *      with the Tcl interpreter.
0160:             *
0161:             *----------------------------------------------------------------------
0162:             */
0163:
0164:            static void EnsembleInit(Interp interp) // interpreter being initialized
0165:            {
0166:                // ItclEnsInvoc obj type need not be registered with Jacl
0167:
0168:                interp.createCommand("::itcl::ensemble", new EnsembleCmd(null));
0169:            }
0170:
0171:            /*
0172:             *----------------------------------------------------------------------
0173:             *
0174:             * Itcl_CreateEnsemble -> Ensemble.CreateEnsemble
0175:             *
0176:             *      Creates an ensemble command, or adds a sub-ensemble to an
0177:             *      existing ensemble command.  The ensemble name is a space-
0178:             *      separated list.  The first word in the list is the command
0179:             *      name for the top-level ensemble.  Other names do not have
0180:             *      commands associated with them; they are merely sub-ensembles
0181:             *      within the ensemble.  So a name like "a::b::foo bar baz"
0182:             *      represents an ensemble command called "foo" in the namespace
0183:             *      "a::b" that has a sub-ensemble "bar", that has a sub-ensemble
0184:             *      "baz".
0185:             *
0186:             *      If the name is a single word, then this procedure creates
0187:             *      a top-level ensemble and installs an access command for it.
0188:             *      If a command already exists with that name, it is deleted.
0189:             *
0190:             *      If the name has more than one word, then the leading words
0191:             *      are treated as a path name for an existing ensemble.  The
0192:             *      last word is treated as the name for a new sub-ensemble.
0193:             *      If an part already exists with that name, it is an error.
0194:             *
0195:             * Results:
0196:             *      Raises a TclException if anything goes wrong.
0197:             *
0198:             *----------------------------------------------------------------------
0199:             */
0200:
0201:            static void CreateEnsemble(Interp interp, // interpreter to be updated
0202:                    String ensName) // name of the new ensemble
0203:                    throws TclException {
0204:                Ensemble parentEnsData = null;
0205:                TclObject list;
0206:                String[] nameArgv = null;
0207:
0208:                //  Split the ensemble name into its path components.
0209:
0210:                try {
0211:                    nameArgv = SplitEnsemble(interp, ensName);
0212:                } catch (TclException ex) {
0213:                    CreateEnsembleFailed(interp, ensName, ex);
0214:                }
0215:                if (nameArgv.length < 1) {
0216:                    TclException ex = new TclException(interp,
0217:                            "invalid ensemble name \"" + ensName + "\"");
0218:                    CreateEnsembleFailed(interp, ensName, ex);
0219:                }
0220:
0221:                //  If there is more than one path component, then follow
0222:                //  the path down to the last component, to find the containing
0223:                //  ensemble.
0224:
0225:                parentEnsData = null;
0226:                if (nameArgv.length > 1) {
0227:                    try {
0228:                        parentEnsData = FindEnsemble(interp, nameArgv,
0229:                                nameArgv.length - 1);
0230:                    } catch (TclException ex) {
0231:                        CreateEnsembleFailed(interp, ensName, ex);
0232:                    }
0233:
0234:                    if (parentEnsData == null) {
0235:                        String pname = MergeEnsemble(interp, nameArgv,
0236:                                nameArgv.length - 1);
0237:                        TclException ex = new TclException(interp,
0238:                                "invalid ensemble name \"" + pname + "\"");
0239:                        CreateEnsembleFailed(interp, ensName, ex);
0240:                    }
0241:                }
0242:
0243:                //  Create the ensemble.
0244:
0245:                try {
0246:                    CreateEnsemble(interp, parentEnsData,
0247:                            nameArgv[nameArgv.length - 1]);
0248:                } catch (TclException ex) {
0249:                    CreateEnsembleFailed(interp, ensName, ex);
0250:                }
0251:            }
0252:
0253:            // Helper function used when CreateEnsemble fails
0254:
0255:            static void CreateEnsembleFailed(Interp interp, String ensName,
0256:                    TclException ex) throws TclException {
0257:                StringBuffer buffer = new StringBuffer(64);
0258:
0259:                buffer.append("\n    (while creating ensemble \"");
0260:                buffer.append(ensName);
0261:                buffer.append("\")");
0262:                interp.addErrorInfo(buffer.toString());
0263:
0264:                throw ex;
0265:            }
0266:
0267:            /*
0268:             *----------------------------------------------------------------------
0269:             *
0270:             * Itcl_AddEnsemblePart -> Ensemble.AddEnsemblePart
0271:             *
0272:             *      Adds a part to an ensemble which has been created by
0273:             *      Itcl_CreateEnsemble.  Ensembles are addressed by name, as
0274:             *      described in Itcl_CreateEnsemble.
0275:             *
0276:             *      If the ensemble already has a part with the specified name,
0277:             *      this procedure returns an error.  Otherwise, it adds a new
0278:             *      part to the ensemble.
0279:             *
0280:             *      Any client data specified is automatically passed to the
0281:             *      handling procedure whenever the part is invoked.  It is
0282:             *      automatically destroyed by the deleteProc when the part is
0283:             *      deleted.
0284:             *
0285:             * Results:
0286:             *      Raises a TclException if anything goes wrong.
0287:             *
0288:             *----------------------------------------------------------------------
0289:             */
0290:
0291:            static void AddEnsemblePart(Interp interp, // interpreter to be updated
0292:                    String ensName, // ensemble containing this part
0293:                    String partName, // name of the new part
0294:                    String usageInfo, // usage info for argument list
0295:                    Command objCmd) // handling procedure for part
0296:                    throws TclException {
0297:                String[] nameArgv = null;
0298:                Ensemble ensData = null;
0299:                EnsemblePart ensPart;
0300:
0301:                //  Parse the ensemble name and look for a containing ensemble.
0302:
0303:                try {
0304:                    nameArgv = SplitEnsemble(interp, ensName);
0305:                } catch (TclException ex) {
0306:                    AddEnsemblePartFailed(interp, ensName, ex);
0307:                }
0308:                try {
0309:                    ensData = FindEnsemble(interp, nameArgv, nameArgv.length);
0310:                } catch (TclException ex) {
0311:                    AddEnsemblePartFailed(interp, ensName, ex);
0312:                }
0313:
0314:                if (ensData == null) {
0315:                    String pname = MergeEnsemble(interp, nameArgv,
0316:                            nameArgv.length);
0317:                    TclException ex = new TclException(interp,
0318:                            "invalid ensemble name \"" + pname + "\"");
0319:                    AddEnsemblePartFailed(interp, ensName, ex);
0320:                }
0321:
0322:                //  Install the new part into the part list.
0323:
0324:                try {
0325:                    ensPart = AddEnsemblePart(interp, ensData, partName,
0326:                            usageInfo, objCmd);
0327:                } catch (TclException ex) {
0328:                    AddEnsemblePartFailed(interp, ensName, ex);
0329:                }
0330:            }
0331:
0332:            // Helper function used when AddEnsemblePart fails
0333:
0334:            static void AddEnsemblePartFailed(Interp interp, String ensName,
0335:                    TclException ex) throws TclException {
0336:                StringBuffer buffer = new StringBuffer();
0337:
0338:                buffer.append("\n    (while adding to ensemble \"");
0339:                buffer.append(ensName);
0340:                buffer.append("\")");
0341:                interp.addErrorInfo(buffer.toString());
0342:
0343:                throw ex;
0344:            }
0345:
0346:            // Note: Itcl_GetEnsemblePart not ported since it seems to be unused
0347:            // Note: Itcl_IsEnsemble not ported since it seems to be unused
0348:
0349:            /*
0350:             *----------------------------------------------------------------------
0351:             *
0352:             * Itcl_GetEnsembleUsage -> Ensemble.GetEnsembleUsage
0353:             *
0354:             *      Returns a summary of all of the parts of an ensemble and
0355:             *      the meaning of their arguments.  Each part is listed on
0356:             *      a separate line.  Having this summary is sometimes useful
0357:             *      when building error messages for the "@error" handler in
0358:             *      an ensemble.
0359:             *
0360:             *      Ensembles are accessed by name, as described in
0361:             *      Itcl_CreateEnsemble.
0362:             *
0363:             * Results:
0364:             *      If the ensemble is found, its usage information is appended
0365:             *      to the buffer argument and the function returns true.
0366:             *      If anything goes wrong, this procedure returns false;
0367:             *
0368:             * Side effects:
0369:             *      Buffer passed in is modified. 
0370:             *
0371:             *----------------------------------------------------------------------
0372:             */
0373:
0374:            static boolean GetEnsembleUsage(Interp interp, // interpreter containing the ensemble
0375:                    String ensName, // name of the ensemble
0376:                    StringBuffer buffer) // returns: summary of usage info
0377:            {
0378:                String[] nameArgv = null;
0379:                Ensemble ensData;
0380:                Itcl_InterpState state;
0381:                String retval;
0382:
0383:                //  Parse the ensemble name and look for the ensemble.
0384:                //  Save the interpreter state before we do this.  If we get
0385:                //  any errors, we don't want them to affect the interpreter.
0386:
0387:                state = Util.SaveInterpState(interp, 0);
0388:
0389:                try {
0390:                    nameArgv = SplitEnsemble(interp, ensName);
0391:                } catch (TclException ex) {
0392:                    Util.RestoreInterpState(interp, state);
0393:                    return false;
0394:                }
0395:
0396:                try {
0397:                    ensData = FindEnsemble(interp, nameArgv, nameArgv.length);
0398:                } catch (TclException ex) {
0399:                    Util.RestoreInterpState(interp, state);
0400:                    return false;
0401:                }
0402:
0403:                if (ensData == null) {
0404:                    Util.RestoreInterpState(interp, state);
0405:                    return false;
0406:                }
0407:
0408:                //  Add a summary of usage information to the return buffer.
0409:
0410:                GetEnsembleUsage(ensData, buffer);
0411:
0412:                Util.DiscardInterpState(state);
0413:
0414:                return true;
0415:            }
0416:
0417:            /*
0418:             *----------------------------------------------------------------------
0419:             *
0420:             * Itcl_GetEnsembleUsageForObj -> Ensemble.GetEnsembleUsageForObj
0421:             *
0422:             *      Returns a summary of all of the parts of an ensemble and
0423:             *      the meaning of their arguments.  This procedure is just
0424:             *      like Itcl_GetEnsembleUsage, but it determines the desired
0425:             *      ensemble from a command line argument.  The argument should
0426:             *      be the first argument on the command line--the ensemble
0427:             *      command or one of its parts.
0428:             *
0429:             * Results:
0430:             *      If the ensemble is found, its usage information is appended
0431:             *      onto the buffer, and this procedure returns true.
0432:             *      It is the responsibility of the caller to init the buffer.
0433:             *      If anything goes wrong, this procedure returns false.
0434:             *
0435:             * Side effects:
0436:             *      Buffer passed in is modified.
0437:             *----------------------------------------------------------------------
0438:             */
0439:
0440:            static boolean GetEnsembleUsageForObj(Interp interp, // interpreter containing the ensemble
0441:                    TclObject ensObj, // argument representing ensemble
0442:                    StringBuffer buffer) {
0443:                Ensemble ensData;
0444:                TclObject chainObj = null;
0445:                Command cmd;
0446:
0447:                //  If the argument is an ensemble part, then follow the chain
0448:                //  back to the command word for the entire ensemble.
0449:
0450:                chainObj = ensObj;
0451:                while (chainObj != null
0452:                        && (chainObj.getInternalRep() instanceof  ItclEnsInvoc)) {
0453:                    ItclEnsInvoc t = (ItclEnsInvoc) chainObj.getInternalRep();
0454:                    chainObj = t.chainObj;
0455:                }
0456:
0457:                if (chainObj != null) {
0458:                    cmd = interp.getCommand(chainObj.toString());
0459:                    if (cmd != null && (cmd instanceof  HandleEnsemble)) {
0460:                        ensData = ((HandleEnsemble) cmd).ensData;
0461:                        GetEnsembleUsage(ensData, buffer);
0462:                        return true;
0463:                    }
0464:                }
0465:                return false;
0466:            }
0467:
0468:            /*
0469:             *----------------------------------------------------------------------
0470:             *
0471:             * GetEnsembleUsage -> Ensemble.GetEnsembleUsage
0472:             *
0473:             *      
0474:             *      Returns a summary of all of the parts of an ensemble and
0475:             *      the meaning of their arguments.  Each part is listed on
0476:             *      a separate line.  This procedure is used internally to
0477:             *      generate usage information for error messages.
0478:             *
0479:             * Results:
0480:             *      Appends usage information onto the bufer.
0481:             *
0482:             * Side effects:
0483:             *      None.
0484:             *
0485:             *----------------------------------------------------------------------
0486:             */
0487:
0488:            static void GetEnsembleUsage(Ensemble ensData, // ensemble data
0489:                    StringBuffer buffer) // returns: summary of usage info
0490:            {
0491:                String spaces = "  ";
0492:                boolean isOpenEnded = false;
0493:
0494:                EnsemblePart ensPart;
0495:
0496:                for (int i = 0; i < ensData.numParts; i++) {
0497:                    ensPart = ensData.parts[i];
0498:
0499:                    if (ensPart.name.equals("@error")) {
0500:                        isOpenEnded = true;
0501:                    } else {
0502:                        buffer.append(spaces);
0503:                        GetEnsemblePartUsage(ensPart, buffer);
0504:                        spaces = "\n  ";
0505:                    }
0506:                }
0507:                if (isOpenEnded) {
0508:                    buffer.append("\n...and others described on the man page");
0509:                }
0510:            }
0511:
0512:            /*
0513:             *----------------------------------------------------------------------
0514:             *
0515:             * GetEnsemblePartUsage -> Ensemble.GetEnsemblePartUsage
0516:             *
0517:             *      Determines the usage for a single part within an ensemble,
0518:             *      and appends a summary onto a dynamic string.  The usage
0519:             *      is a combination of the part name and the argument summary.
0520:             *
0521:             * Results:
0522:             *      Returns usage information in the buffer.
0523:             *
0524:             * Side effects:
0525:             *      None.
0526:             *
0527:             *----------------------------------------------------------------------
0528:             */
0529:
0530:            static void GetEnsemblePartUsage(EnsemblePart ensPart, // ensemble part for usage info
0531:                    StringBuffer buffer) // returns: usage information
0532:            {
0533:                EnsemblePart part;
0534:                WrappedCommand wcmd;
0535:                String name;
0536:                Itcl_List trail;
0537:                Itcl_ListElem elem;
0538:
0539:                //  Build the trail of ensemble names leading to this part.
0540:
0541:                trail = new Itcl_List();
0542:                Util.InitList(trail);
0543:                for (part = ensPart; part != null; part = part.ensemble.parent) {
0544:                    Util.InsertList(trail, part);
0545:                }
0546:
0547:                wcmd = ensPart.ensemble.wcmd;
0548:                name = ensPart.ensemble.interp.getCommandName(wcmd);
0549:                Util.AppendElement(buffer, name);
0550:
0551:                for (elem = Util.FirstListElem(trail); elem != null; elem = Util
0552:                        .NextListElem(elem)) {
0553:                    part = (EnsemblePart) Util.GetListValue(elem);
0554:                    Util.AppendElement(buffer, part.name);
0555:                }
0556:                Util.DeleteList(trail);
0557:
0558:                //  If the part has usage info, use it directly.
0559:
0560:                if (ensPart.usage != null && ensPart.usage.length() > 0) {
0561:                    buffer.append(" ");
0562:                    buffer.append(ensPart.usage);
0563:                }
0564:
0565:                //  If the part is itself an ensemble, summarize its usage.
0566:                else if (ensPart.cmd != null
0567:                        && (ensPart.cmd instanceof  HandleEnsemble)) {
0568:                    buffer.append(" option ?arg arg ...?");
0569:                }
0570:            }
0571:
0572:            /*
0573:             *----------------------------------------------------------------------
0574:             *
0575:             * CreateEnsemble -> Ensemble.CreateEnsemble
0576:             *
0577:             *      Creates an ensemble command, or adds a sub-ensemble to an
0578:             *      existing ensemble command.  Works like Itcl_CreateEnsemble,
0579:             *      except that the ensemble name is a single name, not a path.
0580:             *      If a parent ensemble is specified, then a new ensemble is
0581:             *      added to that parent.  If a part already exists with the
0582:             *      same name, it is an error.  If a parent ensemble is not
0583:             *      specified, then a top-level ensemble is created.  If a
0584:             *      command already exists with the same name, it is deleted.
0585:             *
0586:             * Results:
0587:             *      If anything goes wrong, this procedure raises a TclException
0588:             *
0589:             * Side effects:
0590:             *      None.
0591:             *
0592:             *----------------------------------------------------------------------
0593:             */
0594:
0595:            static void CreateEnsemble(Interp interp, // interpreter to be updated
0596:                    Ensemble parentEnsData, // parent ensemble or null
0597:                    String ensName) // name of the new ensemble
0598:                    throws TclException {
0599:                Ensemble ensData;
0600:                EnsemblePart ensPart;
0601:                WrappedCommand wcmd;
0602:
0603:                //  Create the data associated with the ensemble.
0604:
0605:                ensData = new Ensemble();
0606:                ensData.interp = interp;
0607:                ensData.numParts = 0;
0608:                ensData.maxParts = 10;
0609:                ensData.parts = new EnsemblePart[ensData.maxParts];
0610:                ensData.wcmd = null;
0611:                ensData.parent = null;
0612:
0613:                //  If there is no parent data, then this is a top-level
0614:                //  ensemble.  Create the ensemble by installing its access
0615:                //  command.
0616:                //
0617:                if (parentEnsData == null) {
0618:                    interp.createCommand(ensName, new HandleEnsemble(ensData));
0619:                    wcmd = Namespace.findCommand(interp, ensName, null,
0620:                            TCL.NAMESPACE_ONLY);
0621:                    ensData.wcmd = wcmd;
0622:                    return;
0623:                }
0624:
0625:                //  Otherwise, this ensemble is contained within another parent.
0626:                //  Install the new ensemble as a part within its parent.
0627:
0628:                try {
0629:                    ensPart = CreateEnsemblePart(interp, parentEnsData, ensName);
0630:                } catch (TclException ex) {
0631:                    DeleteEnsemble(ensData);
0632:                    throw ex;
0633:                }
0634:
0635:                ensData.wcmd = parentEnsData.wcmd;
0636:                ensData.parent = ensPart;
0637:
0638:                // For an ensemble part that is itself an ensemble,
0639:                // create an instance of HandleInstance and associate
0640:                // it with the Ensemble instance. Note that the
0641:                // Command instance is not installed into the interpreter.
0642:
0643:                ensPart.cmd = new HandleEnsemble(ensData);
0644:            }
0645:
0646:            /*
0647:             *----------------------------------------------------------------------
0648:             *
0649:             * AddEnsemblePart -> Ensemble.AddEnsemblePart
0650:             *
0651:             *      Adds a part to an existing ensemble.  Works like
0652:             *      Itcl_AddEnsemblePart, but the part name is a single word,
0653:             *      not a path.
0654:             *
0655:             *      If the ensemble already has a part with the specified name,
0656:             *      this procedure returns an error.  Otherwise, it adds a new
0657:             *      part to the ensemble.
0658:             *
0659:             *      Any client data specified is automatically passed to the
0660:             *      handling procedure whenever the part is invoked.  It is
0661:             *      automatically destroyed by the deleteProc when the part is
0662:             *      deleted.
0663:             *
0664:             * Results:
0665:             *      If anything goes wrong, this procedure raises a TclException
0666:             *
0667:             * Side effects:
0668:             *      None.
0669:             *
0670:             *----------------------------------------------------------------------
0671:             */
0672:
0673:            static EnsemblePart AddEnsemblePart(Interp interp, // interpreter to be updated
0674:                    Ensemble ensData, // ensemble that will contain this part
0675:                    String partName, // name of the new part
0676:                    String usageInfo, // usage info for argument list
0677:                    Command objProc) // handling procedure for part
0678:                    throws TclException {
0679:                EnsemblePart ensPart;
0680:                WrappedCommand wcmd;
0681:
0682:                //  Install the new part into the part list.
0683:
0684:                ensPart = CreateEnsemblePart(interp, ensData, partName);
0685:
0686:                if (usageInfo != null) {
0687:                    ensPart.usage = usageInfo;
0688:                }
0689:
0690:                // Install the passed in Command in the ensemble part.
0691:
0692:                wcmd = new WrappedCommand();
0693:                wcmd.ns = ensData.wcmd.ns;
0694:                wcmd.cmd = objProc;
0695:                ensPart.cmd = objProc;
0696:                ensPart.wcmd = wcmd;
0697:
0698:                return ensPart;
0699:            }
0700:
0701:            /*
0702:             *----------------------------------------------------------------------
0703:             *
0704:             * DeleteEnsemble -> Ensemble.DeleteEnsemble
0705:             *
0706:             *      Invoked when the command associated with an ensemble is
0707:             *      destroyed, to delete the ensemble.  Destroys all parts
0708:             *      included in the ensemble, and frees all memory associated
0709:             *      with it.
0710:             *
0711:             * Results:
0712:             *      None.
0713:             *
0714:             * Side effects:
0715:             *      None.
0716:             *
0717:             *----------------------------------------------------------------------
0718:             */
0719:
0720:            static void DeleteEnsemble(Ensemble ensData) {
0721:                //  BE CAREFUL:  Each ensemble part removes itself from the list.
0722:                //  So keep deleting the first part until all parts are gone.
0723:                while (ensData.numParts > 0) {
0724:                    DeleteEnsemblePart(ensData.parts[0]);
0725:                }
0726:                ensData.parts = null;
0727:            }
0728:
0729:            /*
0730:             *----------------------------------------------------------------------
0731:             *
0732:             * FindEnsemble -> Ensemble.FindEnsemble
0733:             *
0734:             *      Searches for an ensemble command and follows a path to
0735:             *      sub-ensembles.
0736:             *
0737:             * Results:
0738:             *      If the ensemble name is invalid then null will be returned.
0739:             *      If anything goes wrong, this procedure raises a TclException
0740:             *
0741:             * Side effects:
0742:             *      None.
0743:             *
0744:             *----------------------------------------------------------------------
0745:             */
0746:
0747:            static Ensemble FindEnsemble(Interp interp, // interpreter containing the ensemble
0748:                    String[] nameArgv, // path of names leading to ensemble
0749:                    int nameArgc) // number of nameArgv to process
0750:                    throws TclException {
0751:                WrappedCommand wcmd;
0752:                Command cmd;
0753:                Ensemble ensData;
0754:                EnsemblePart ensPart;
0755:
0756:                //  If there are no names in the path, then return an error.
0757:
0758:                if (nameArgc < 1) {
0759:                    return null; // Caller should create "invalid ensemble" error.
0760:                }
0761:
0762:                //  Use the first name to find the command for the top-level
0763:                //  ensemble.
0764:
0765:                wcmd = Namespace.findCommand(interp, nameArgv[0], null,
0766:                        TCL.LEAVE_ERR_MSG);
0767:
0768:                if (wcmd == null || !(wcmd.cmd instanceof  HandleEnsemble)) {
0769:                    throw new TclException(interp, "command \"" + nameArgv[0]
0770:                            + "\" is not an ensemble");
0771:                }
0772:                ensData = ((HandleEnsemble) wcmd.cmd).ensData;
0773:
0774:                //  Follow the trail of sub-ensemble names.
0775:
0776:                for (int i = 1; i < nameArgc; i++) {
0777:                    ensPart = FindEnsemblePart(interp, ensData, nameArgv[i]);
0778:                    if (ensPart == null) {
0779:                        String pname = MergeEnsemble(interp, nameArgv, i);
0780:                        TclException ex = new TclException(interp,
0781:                                "invalid ensemble name \"" + pname + "\"");
0782:                    }
0783:
0784:                    cmd = ensPart.cmd;
0785:                    if (cmd == null || !(cmd instanceof  HandleEnsemble)) {
0786:                        throw new TclException(interp, "part \"" + nameArgv[i]
0787:                                + "\" is not an ensemble");
0788:                    }
0789:                    ensData = ((HandleEnsemble) cmd).ensData;
0790:                }
0791:
0792:                return ensData;
0793:            }
0794:
0795:            /*
0796:             *----------------------------------------------------------------------
0797:             *
0798:             * CreateEnsemblePart -> Ensemble.CreateEnsemblePart
0799:             *
0800:             *      Creates a new part within an ensemble.
0801:             *
0802:             * Results:
0803:             *      If anything goes wrong, this procedure raises a TclException
0804:             *
0805:             * Side effects:
0806:             *      None.
0807:             *
0808:             *----------------------------------------------------------------------
0809:             */
0810:
0811:            static EnsemblePart CreateEnsemblePart(Interp interp, // interpreter containing the ensemble
0812:                    Ensemble ensData, // ensemble being modified
0813:                    String partName) // name of the new part
0814:                    throws TclException {
0815:                int i, pos;
0816:                EnsemblePart[] partList;
0817:                EnsemblePart part;
0818:
0819:                //  If a matching entry was found, then return an error.
0820:
0821:                FindEnsemblePartIndexResult res = FindEnsemblePartIndex(
0822:                        ensData, partName);
0823:
0824:                if (res.status) {
0825:                    throw new TclException(interp, "part \"" + partName
0826:                            + "\" already exists in ensemble");
0827:                }
0828:                pos = res.pos;
0829:
0830:                //  Otherwise, make room for a new entry.  Keep the parts in
0831:                //  lexicographical order, so we can search them quickly
0832:                //  later.
0833:
0834:                if (ensData.numParts >= ensData.maxParts) {
0835:                    partList = new EnsemblePart[ensData.maxParts * 2];
0836:                    for (i = 0; i < ensData.maxParts; i++) {
0837:                        partList[i] = ensData.parts[i];
0838:                    }
0839:                    ensData.parts = null;
0840:                    ensData.parts = partList;
0841:                    ensData.maxParts = partList.length;
0842:                }
0843:
0844:                for (i = ensData.numParts; i > pos; i--) {
0845:                    ensData.parts[i] = ensData.parts[i - 1];
0846:                }
0847:                ensData.numParts++;
0848:
0849:                part = new EnsemblePart();
0850:                part.name = partName;
0851:                part.cmd = null;
0852:                part.usage = null;
0853:                part.ensemble = ensData;
0854:
0855:                ensData.parts[pos] = part;
0856:
0857:                //  Compare the new part against the one on either side of
0858:                //  it.  Determine how many letters are needed in each part
0859:                //  to guarantee that an abbreviated form is unique.  Update
0860:                //  the parts on either side as well, since they are influenced
0861:                //  by the new part.
0862:
0863:                ComputeMinChars(ensData, pos);
0864:                ComputeMinChars(ensData, pos - 1);
0865:                ComputeMinChars(ensData, pos + 1);
0866:
0867:                return part;
0868:            }
0869:
0870:            /*
0871:             *----------------------------------------------------------------------
0872:             *
0873:             * DeleteEnsemblePart -> Ensemble.DeleteEnsemblePart
0874:             *
0875:             *      Deletes a single part from an ensemble.  The part must have 
0876:             *      been created previously by CreateEnsemblePart.
0877:             *
0878:             *      Invoke disposeCmd() if the part has a delete callback.
0879:             *
0880:             * Results:
0881:             *      None.
0882:             *
0883:             * Side effects:
0884:             *      Delete proc is called.
0885:             *
0886:             *----------------------------------------------------------------------
0887:             */
0888:
0889:            static void DeleteEnsemblePart(EnsemblePart ensPart) // part being destroyed
0890:            {
0891:                int i, pos;
0892:                Ensemble ensData;
0893:                Command cmd = ensPart.cmd;
0894:
0895:                //  If this part has a delete proc, then call it to free
0896:                //  up the client data.
0897:
0898:                if (cmd instanceof  CommandWithDispose) {
0899:                    ((CommandWithDispose) cmd).disposeCmd();
0900:                }
0901:                ensPart.cmd = null;
0902:
0903:                //  Find this part within its ensemble, and remove it from
0904:                //  the list of parts.
0905:
0906:                FindEnsemblePartIndexResult res = FindEnsemblePartIndex(
0907:                        ensPart.ensemble, ensPart.name);
0908:
0909:                if (res.status) {
0910:                    pos = res.pos;
0911:                    ensData = ensPart.ensemble;
0912:                    for (i = pos; i < ensData.numParts - 1; i++) {
0913:                        ensData.parts[i] = ensData.parts[i + 1];
0914:                    }
0915:                    ensData.numParts--;
0916:                }
0917:
0918:                //  Free the memory associated with the part.
0919:
0920:                if (ensPart.usage != null) {
0921:                    ensPart.usage = null;
0922:                }
0923:                ensPart.name = null;
0924:            }
0925:
0926:            /*
0927:             *----------------------------------------------------------------------
0928:             *
0929:             * FindEnsemblePart -> Ensemble.FindEnsemblePart
0930:             *
0931:             *      Searches for a part name within an ensemble.  Recognizes
0932:             *      unique abbreviations for part names.
0933:             *
0934:             * Results:
0935:             *      If the part name is not a unique abbreviation, this procedure
0936:             *      raises a TclException. If the part can be found, returns a
0937:             *      reference to the part. Otherwise, it returns NULL.
0938:             *
0939:             * Side effects:
0940:             *      None.
0941:             *
0942:             *----------------------------------------------------------------------
0943:             */
0944:
0945:            static EnsemblePart FindEnsemblePart(Interp interp, // interpreter containing the ensemble
0946:                    Ensemble ensData, // ensemble being searched
0947:                    String partName) // name of the desired part
0948:                    throws TclException {
0949:                int pos = 0;
0950:                int first, last, nlen;
0951:                int i, cmp;
0952:                EnsemblePart rensPart = null;
0953:
0954:                //  Search for the desired part name.
0955:                //  All parts are in lexicographical order, so use a
0956:                //  binary search to find the part quickly.  Match only
0957:                //  as many characters as are included in the specified
0958:                //  part name.
0959:
0960:                first = 0;
0961:                last = ensData.numParts - 1;
0962:                nlen = partName.length();
0963:
0964:                while (last >= first) {
0965:                    pos = (first + last) / 2;
0966:                    if (partName.charAt(0) == ensData.parts[pos].name.charAt(0)) {
0967:                        cmp = partName.substring(0, nlen).compareTo(
0968:                                ensData.parts[pos].name);
0969:                        if (cmp == 0) {
0970:                            break; // found it!
0971:                        }
0972:                    } else if (partName.charAt(0) < ensData.parts[pos].name
0973:                            .charAt(0)) {
0974:                        cmp = -1;
0975:                    } else {
0976:                        cmp = 1;
0977:                    }
0978:
0979:                    if (cmp > 0) {
0980:                        first = pos + 1;
0981:                    } else {
0982:                        last = pos - 1;
0983:                    }
0984:                }
0985:
0986:                //  If a matching entry could not be found, then quit.
0987:
0988:                if (last < first) {
0989:                    return rensPart;
0990:                }
0991:
0992:                //  If a matching entry was found, there may be some ambiguity
0993:                //  if the user did not specify enough characters.  Find the
0994:                //  top-most match in the list, and see if the part name has
0995:                //  enough characters.  If there are two parts like "foo"
0996:                //  and "food", this allows us to match "foo" exactly.
0997:
0998:                if (nlen < ensData.parts[pos].minChars) {
0999:                    while (pos > 0) {
1000:                        pos--;
1001:                        if (partName.substring(0, nlen).compareTo(
1002:                                ensData.parts[pos].name) != 0) {
1003:                            pos++;
1004:                            break;
1005:                        }
1006:                    }
1007:                }
1008:                if (nlen < ensData.parts[pos].minChars) {
1009:                    StringBuffer buffer = new StringBuffer(64);
1010:
1011:                    buffer.append("ambiguous option \"" + partName
1012:                            + "\": should be one of...");
1013:
1014:                    for (i = pos; i < ensData.numParts; i++) {
1015:                        if (partName.substring(0, nlen).compareTo(
1016:                                ensData.parts[i].name) != 0) {
1017:                            break;
1018:                        }
1019:                        buffer.append("\n  ");
1020:                        GetEnsemblePartUsage(ensData.parts[i], buffer);
1021:                    }
1022:                    throw new TclException(interp, buffer.toString());
1023:                }
1024:
1025:                //  Found a match.  Return the desired part.
1026:
1027:                rensPart = ensData.parts[pos];
1028:                return rensPart;
1029:            }
1030:
1031:            /*
1032:             *----------------------------------------------------------------------
1033:             *
1034:             * FindEnsemblePartIndex -> Ensemble.FindEnsemblePartIndex
1035:             *
1036:             *      Searches for a part name within an ensemble.  The part name
1037:             *      must be an exact match for an existing part name in the
1038:             *      ensemble.  This procedure is useful for managing (i.e.,
1039:             *      creating and deleting) parts in an ensemble.
1040:             *
1041:             * Results:
1042:             *      If an exact match is found, this procedure returns
1043:             *      a status of true, along with the index of the part in pos.
1044:             *      Otherwise, it returns a status of false, along with an
1045:             *      index in pos indicating where the part should be.
1046:             *
1047:             * Side effects:
1048:             *      None.
1049:             *
1050:             *----------------------------------------------------------------------
1051:             */
1052:
1053:            static FindEnsemblePartIndexResult FindEnsemblePartIndex(
1054:                    Ensemble ensData, // ensemble being searched
1055:                    String partName) // name of desired part
1056:            {
1057:                int pos = 0;
1058:                int first, last;
1059:                int cmp;
1060:                int posRes;
1061:
1062:                //  Search for the desired part name.
1063:                //  All parts are in lexicographical order, so use a
1064:                //  binary search to find the part quickly.
1065:
1066:                first = 0;
1067:                last = ensData.numParts - 1;
1068:
1069:                while (last >= first) {
1070:                    pos = (first + last) / 2;
1071:                    if (partName.charAt(0) == ensData.parts[pos].name.charAt(0)) {
1072:                        cmp = partName.compareTo(ensData.parts[pos].name);
1073:                        if (cmp == 0) {
1074:                            break; // found it!
1075:                        }
1076:                    } else if (partName.charAt(0) < ensData.parts[pos].name
1077:                            .charAt(0)) {
1078:                        cmp = -1;
1079:                    } else {
1080:                        cmp = 1;
1081:                    }
1082:
1083:                    if (cmp > 0) {
1084:                        first = pos + 1;
1085:                    } else {
1086:                        last = pos - 1;
1087:                    }
1088:                }
1089:
1090:                FindEnsemblePartIndexResult res = new FindEnsemblePartIndexResult();
1091:
1092:                if (last >= first) {
1093:                    res.status = true;
1094:                    res.pos = pos;
1095:                    return res;
1096:                }
1097:                res.status = false;
1098:                res.pos = first;
1099:                return res;
1100:            }
1101:
1102:            static class FindEnsemblePartIndexResult {
1103:                boolean status;
1104:                int pos;
1105:            }
1106:
1107:            /*
1108:             *----------------------------------------------------------------------
1109:             *
1110:             * ComputeMinChars -> Ensemble.ComputeMinChars
1111:             *
1112:             *      Compares part names on an ensemble's part list and
1113:             *      determines the minimum number of characters needed for a
1114:             *      unique abbreviation.  The parts on either side of a
1115:             *      particular part index are compared.  As long as there is
1116:             *      a part on one side or the other, this procedure updates
1117:             *      the parts to have the proper minimum abbreviations.
1118:             *
1119:             * Results:
1120:             *      None.
1121:             *
1122:             * Side effects:
1123:             *      Updates three parts within the ensemble to remember
1124:             *      the minimum abbreviations.
1125:             *
1126:             *----------------------------------------------------------------------
1127:             */
1128:
1129:            static void ComputeMinChars(Ensemble ensData, // ensemble being modified
1130:                    int pos) // index of part being updated
1131:            {
1132:                int min, max;
1133:                int p, q;
1134:                String pstr, qstr;
1135:
1136:                //  If the position is invalid, do nothing.
1137:
1138:                if (pos < 0 || pos >= ensData.numParts) {
1139:                    return;
1140:                }
1141:
1142:                //  Start by assuming that only the first letter is required
1143:                //  to uniquely identify this part.  Then compare the name
1144:                //  against each neighboring part to determine the real minimum.
1145:
1146:                ensData.parts[pos].minChars = 1;
1147:
1148:                if (pos - 1 >= 0) {
1149:                    pstr = ensData.parts[pos].name;
1150:                    p = 0;
1151:                    qstr = ensData.parts[pos - 1].name;
1152:                    q = 0;
1153:                    final int plen = pstr.length();
1154:                    final int qlen = qstr.length();
1155:                    for (min = 1; p < plen && q < qlen
1156:                            && pstr.charAt(p) == qstr.charAt(q); min++) {
1157:                        p++;
1158:                        q++;
1159:                    }
1160:                    if (min > ensData.parts[pos].minChars) {
1161:                        ensData.parts[pos].minChars = min;
1162:                    }
1163:                }
1164:
1165:                if (pos + 1 < ensData.numParts) {
1166:                    pstr = ensData.parts[pos].name;
1167:                    p = 0;
1168:                    qstr = ensData.parts[pos + 1].name;
1169:                    q = 0;
1170:                    final int plen = pstr.length();
1171:                    final int qlen = qstr.length();
1172:                    for (min = 1; p < plen && q < qlen
1173:                            && pstr.charAt(p) == qstr.charAt(q); min++) {
1174:                        p++;
1175:                        q++;
1176:                    }
1177:                    if (min > ensData.parts[pos].minChars) {
1178:                        ensData.parts[pos].minChars = min;
1179:                    }
1180:                }
1181:
1182:                max = ensData.parts[pos].name.length();
1183:                if (ensData.parts[pos].minChars > max) {
1184:                    ensData.parts[pos].minChars = max;
1185:                }
1186:            }
1187:
1188:            /*
1189:             *----------------------------------------------------------------------
1190:             *
1191:             * HandleEnsemble -> Ensemble.HandleEnsemble.cmdProc
1192:             *
1193:             *      Invoked by Tcl whenever the user issues an ensemble-style
1194:             *      command.  Handles commands of the form:
1195:             *
1196:             *        <ensembleName> <partName> ?<arg> <arg>...?
1197:             *
1198:             *      Looks for the <partName> within the ensemble, and if it
1199:             *      exists, the procedure transfers control to it.
1200:             *
1201:             * Results:
1202:             *      If anything goes wrong, this procedure raises a TclException.
1203:             *
1204:             * Side effects:
1205:             *      None.
1206:             *
1207:             *----------------------------------------------------------------------
1208:             */
1209:
1210:            static class HandleEnsemble implements  CommandWithDispose {
1211:                Ensemble ensData;
1212:
1213:                HandleEnsemble(Ensemble ensData) {
1214:                    this .ensData = ensData;
1215:                }
1216:
1217:                public void disposeCmd() {
1218:                    DeleteEnsemble(ensData);
1219:                }
1220:
1221:                public void cmdProc(Interp interp, // Current interp.
1222:                        TclObject[] objv) // Args passed to the command.
1223:                        throws TclException {
1224:                    Command cmd;
1225:                    EnsemblePart ensPart;
1226:                    String partName;
1227:                    final int partNameLen;
1228:                    //Tcl_Obj *cmdlinePtr, *chainObj;
1229:                    //int cmdlinec;
1230:                    //Tcl_Obj **cmdlinev;
1231:                    TclObject cmdline, chainObj;
1232:                    TclObject[] cmdlinev;
1233:
1234:                    //  If a part name is not specified, return an error that
1235:                    //  summarizes the usage for this ensemble.
1236:
1237:                    if (objv.length < 2) {
1238:                        StringBuffer buffer = new StringBuffer(64);
1239:                        buffer.append("wrong # args: should be one of...\n");
1240:                        GetEnsembleUsage(ensData, buffer);
1241:
1242:                        throw new TclException(interp, buffer.toString());
1243:                    }
1244:
1245:                    //  Lookup the desired part.  If an ambiguous abbrevition is
1246:                    //  found, return an error immediately.
1247:
1248:                    partName = objv[1].toString();
1249:                    partNameLen = partName.length();
1250:                    ensPart = FindEnsemblePart(interp, ensData, partName);
1251:
1252:                    //  If the part was not found, then look for an "@error" part
1253:                    //  to handle the error.
1254:
1255:                    if (ensPart == null) {
1256:                        ensPart = FindEnsemblePart(interp, ensData, "@error");
1257:                        if (ensPart != null) {
1258:                            cmd = ensPart.cmd;
1259:                            cmd.cmdProc(interp, objv);
1260:                            return;
1261:                        }
1262:                    }
1263:                    if (ensPart == null) {
1264:                        EnsembleErrorCmd(ensData, interp, objv, 1);
1265:                    }
1266:
1267:                    //  Pass control to the part, and return the result.
1268:
1269:                    chainObj = ItclEnsInvoc.newInstance();
1270:                    ItclEnsInvoc irep = (ItclEnsInvoc) chainObj
1271:                            .getInternalRep();
1272:                    irep.ensPart = ensPart;
1273:                    irep.chainObj = objv[0];
1274:
1275:                    objv[1].preserve();
1276:                    objv[0].preserve();
1277:
1278:                    cmdline = TclList.newInstance();
1279:                    TclList.append(interp, cmdline, chainObj);
1280:
1281:                    for (int i = 2; i < objv.length; i++) {
1282:                        TclList.append(interp, cmdline, objv[i]);
1283:                    }
1284:                    cmdline.preserve();
1285:
1286:                    try {
1287:                        cmdlinev = TclList.getElements(interp, cmdline);
1288:
1289:                        cmd = ensPart.cmd;
1290:                        cmd.cmdProc(interp, cmdlinev);
1291:                    } finally {
1292:                        cmdline.release();
1293:                    }
1294:                }
1295:            } // end class HandleEnsemble
1296:
1297:            /*
1298:             *----------------------------------------------------------------------
1299:             *
1300:             * Itcl_EnsembleCmd -> Ensemble.EnsembleCmd.cmdProc
1301:             *
1302:             *      Invoked by Tcl whenever the user issues the "ensemble"
1303:             *      command to manipulate an ensemble.  Handles the following
1304:             *      syntax:
1305:             *
1306:             *        ensemble <ensName> ?<command> <arg> <arg>...?
1307:             *        ensemble <ensName> {
1308:             *            part <partName> <args> <body>
1309:             *            ensemble <ensName> {
1310:             *                ...
1311:             *            }
1312:             *        }
1313:             *
1314:             *      Finds or creates the ensemble <ensName>, and then executes
1315:             *      the commands to add parts.
1316:             *
1317:             * Results:
1318:             *      If anything goes wrong, this procedure raises a TclException.
1319:             *
1320:             * Side effects:
1321:             *      None.
1322:             *
1323:             *----------------------------------------------------------------------
1324:             */
1325:
1326:            static class EnsembleCmd implements  Command {
1327:                EnsembleParser ensParser;
1328:
1329:                EnsembleCmd(EnsembleParser ensParser) {
1330:                    this .ensParser = ensParser;
1331:                }
1332:
1333:                public void cmdProc(Interp interp, // Current interp.
1334:                        TclObject[] objv) // Args passed to the command.
1335:                        throws TclException {
1336:                    String ensName;
1337:                    EnsembleParser ensInfo;
1338:                    Ensemble ensData, savedEnsData;
1339:                    EnsemblePart ensPart;
1340:                    WrappedCommand wcmd;
1341:                    Command cmd;
1342:
1343:                    //  Make sure that an ensemble name was specified.
1344:
1345:                    if (objv.length < 2) {
1346:                        StringBuffer buffer = new StringBuffer(64);
1347:                        buffer.append("wrong # args: should be \"");
1348:                        buffer.append(objv[0].toString());
1349:                        buffer.append(" name ?command arg arg...?\"");
1350:                        throw new TclException(interp, buffer.toString());
1351:                    }
1352:
1353:                    //  If this is the "ensemble" command in the main interpreter,
1354:                    //  then the client data will be null.  Otherwise, it is
1355:                    //  the "ensemble" command in the ensemble body parser, and
1356:                    //  the client data indicates which ensemble we are modifying.
1357:
1358:                    if (ensParser != null) {
1359:                        ensInfo = ensParser;
1360:                    } else {
1361:                        ensInfo = GetEnsembleParser(interp);
1362:                    }
1363:                    ensData = ensInfo.ensData;
1364:
1365:                    //  Find or create the desired ensemble.  If an ensemble is
1366:                    //  being built, then this "ensemble" command is enclosed in
1367:                    //  another "ensemble" command.  Use the current ensemble as
1368:                    //  the parent, and find or create an ensemble part within it.
1369:
1370:                    ensName = objv[1].toString();
1371:
1372:                    if (ensData != null) {
1373:                        try {
1374:                            ensPart = FindEnsemblePart(interp, ensData, ensName);
1375:                        } catch (TclException ex) {
1376:                            ensPart = null;
1377:                        }
1378:                        if (ensPart == null) {
1379:                            CreateEnsemble(interp, ensData, ensName);
1380:                            try {
1381:                                ensPart = FindEnsemblePart(interp, ensData,
1382:                                        ensName);
1383:                            } catch (TclException ex) {
1384:                                ensPart = null;
1385:                            }
1386:                            Util.Assert(ensPart != null,
1387:                                    "Itcl_EnsembleCmd: can't create ensemble");
1388:                        }
1389:
1390:                        cmd = ensPart.cmd;
1391:                        if (cmd == null || !(cmd instanceof  HandleEnsemble)) {
1392:                            throw new TclException(interp, "part \""
1393:                                    + objv[1].toString()
1394:                                    + "\" is not an ensemble");
1395:                        }
1396:                        ensData = ((HandleEnsemble) cmd).ensData;
1397:                    }
1398:
1399:                    //  Otherwise, the desired ensemble is a top-level ensemble.
1400:                    //  Find or create the access command for the ensemble, and
1401:                    //  then get its data.
1402:
1403:                    else {
1404:                        try {
1405:                            wcmd = Namespace.findCommand(interp, ensName, null,
1406:                                    0);
1407:                        } catch (TclException ex) {
1408:                            wcmd = null;
1409:                        }
1410:                        if (wcmd == null) {
1411:                            CreateEnsemble(interp, null, ensName);
1412:                            wcmd = Namespace.findCommand(interp, ensName, null,
1413:                                    0);
1414:                        }
1415:                        if (wcmd == null) {
1416:                            cmd = null;
1417:                        } else {
1418:                            cmd = wcmd.cmd;
1419:                        }
1420:
1421:                        if (cmd == null || !(cmd instanceof  HandleEnsemble)) {
1422:                            throw new TclException(interp, "command \""
1423:                                    + objv[1].toString()
1424:                                    + "\" is not an ensemble");
1425:                        }
1426:                        ensData = ((HandleEnsemble) cmd).ensData;
1427:                    }
1428:
1429:                    //  At this point, we have the data for the ensemble that is
1430:                    //  being manipulated.  Plug this into the parser, and then
1431:                    //  interpret the rest of the arguments in the ensemble parser. 
1432:
1433:                    TclException evalEx = null;
1434:                    savedEnsData = ensInfo.ensData;
1435:                    ensInfo.ensData = ensData;
1436:
1437:                    if (objv.length == 3) {
1438:                        try {
1439:                            ensInfo.parser.eval(objv[2].toString());
1440:                        } catch (TclException ex) {
1441:                            evalEx = ex;
1442:                        }
1443:                    } else if (objv.length > 3) {
1444:                        TclObject tlist = TclList.newInstance();
1445:                        for (int i = 2; i < objv.length; i++) {
1446:                            TclList.append(interp, tlist, objv[i]);
1447:                        }
1448:                        try {
1449:                            ensInfo.parser.eval(tlist.toString());
1450:                        } catch (TclException ex) {
1451:                            evalEx = ex;
1452:                        }
1453:                    }
1454:
1455:                    //  Copy the result from the parser interpreter to the
1456:                    //  master interpreter.  If an error was encountered,
1457:                    //  copy the error info first, and then set the result.
1458:                    //  Otherwise, the offending command is reported twice.
1459:
1460:                    if (evalEx != null) {
1461:                        TclObject errInfoObj = ensInfo.parser.getVar(
1462:                                "::errorInfo", TCL.GLOBAL_ONLY);
1463:
1464:                        if (errInfoObj != null) {
1465:                            interp.addErrorInfo(errInfoObj.toString());
1466:                        }
1467:
1468:                        if (objv.length == 3) {
1469:                            String msg = "\n    (\"ensemble\" body line "
1470:                                    + ensInfo.parser.getErrorLine() + ")";
1471:                            interp.addErrorInfo(msg);
1472:                        }
1473:
1474:                        ensInfo.ensData = savedEnsData;
1475:                        throw new TclException(interp, ensInfo.parser
1476:                                .getResult().toString());
1477:                    }
1478:
1479:                    ensInfo.ensData = savedEnsData;
1480:                    interp.setResult(ensInfo.parser.getResult().toString());
1481:                }
1482:            } // end class EnsembleCmd
1483:
1484:            /*
1485:             *----------------------------------------------------------------------
1486:             *
1487:             * GetEnsembleParser -> Ensemble.GetEnsembleParser
1488:             *
1489:             *      Returns the slave interpreter that acts as a parser for
1490:             *      the body of an "ensemble" definition.  The first time that
1491:             *      this is called for an interpreter, the parser is created
1492:             *      and registered as associated data.  After that, it is
1493:             *      simply returned.
1494:             *
1495:             * Results:
1496:             *      Returns a reference to the ensemble parser data structure.
1497:             *
1498:             * Side effects:
1499:             *      On the first call, the ensemble parser is created and
1500:             *      registered as "itcl_ensembleParser" with the interpreter.
1501:             *
1502:             *----------------------------------------------------------------------
1503:             */
1504:
1505:            static EnsembleParser GetEnsembleParser(Interp interp) // interpreter handling the ensemble
1506:            {
1507:                Namespace ns, childNs;
1508:                EnsembleParser ensInfo;
1509:                WrappedCommand wcmd;
1510:
1511:                //  Look for an existing ensemble parser.  If it is found,
1512:                //  return it immediately.
1513:
1514:                ensInfo = (EnsembleParser) interp
1515:                        .getAssocData("itcl_ensembleParser");
1516:
1517:                if (ensInfo != null) {
1518:                    return ensInfo;
1519:                }
1520:
1521:                //  Create a slave interpreter that can be used to parse
1522:                //  the body of an ensemble definition.
1523:
1524:                ensInfo = new EnsembleParser();
1525:                ensInfo.master = interp;
1526:                ensInfo.parser = new Interp();
1527:                ensInfo.ensData = null;
1528:
1529:                //  Remove all namespaces and all normal commands from the
1530:                //  parser interpreter.
1531:
1532:                ns = Namespace.getGlobalNamespace(ensInfo.parser);
1533:
1534:                while ((childNs = (Namespace) ItclAccess
1535:                        .FirstHashEntry(ns.childTable)) != null) {
1536:                    Namespace.deleteNamespace(childNs);
1537:                }
1538:
1539:                while ((wcmd = (WrappedCommand) ItclAccess
1540:                        .FirstHashEntry(ns.cmdTable)) != null) {
1541:                    ensInfo.parser.deleteCommandFromToken(wcmd);
1542:                }
1543:
1544:                //  Add the allowed commands to the parser interpreter:
1545:                //  part, delete, ensemble
1546:
1547:                ensInfo.parser.createCommand("part", new EnsPartCmd(ensInfo));
1548:
1549:                ensInfo.parser.createCommand("option", new EnsPartCmd(ensInfo));
1550:
1551:                ensInfo.parser.createCommand("ensemble", new EnsembleCmd(
1552:                        ensInfo));
1553:
1554:                //  Install the parser data, so we'll have it the next time
1555:                //  we call this procedure.
1556:
1557:                interp.setAssocData("itcl_ensembleParser", ensInfo);
1558:
1559:                return ensInfo;
1560:            }
1561:
1562:            /*
1563:             *----------------------------------------------------------------------
1564:             *
1565:             * DeleteEnsParser -> Ensemble.DeleteEnsParser
1566:             *
1567:             *      Called when an interpreter is destroyed to clean up the
1568:             *      ensemble parser within it.  Destroys the slave interpreter
1569:             *      and frees up the data associated with it.
1570:             *
1571:             * Results:
1572:             *      None.
1573:             *
1574:             * Side effects:
1575:             *      None.
1576:             *
1577:             *----------------------------------------------------------------------
1578:             */
1579:
1580:            static void DeleteEnsParser(EnsembleParser ensInfo, // parser for ensemble-related commands
1581:                    Interp interp) // interpreter containing the data
1582:            {
1583:                ensInfo.parser.dispose();
1584:            }
1585:
1586:            /*
1587:             *----------------------------------------------------------------------
1588:             *
1589:             * Itcl_EnsPartCmd -> Ensemble.EnsPartCmd.cmdProc
1590:             *
1591:             *      Invoked by Tcl whenever the user issues the "part" command
1592:             *      to manipulate an ensemble.  This command can only be used
1593:             *      inside the "ensemble" command, which handles ensembles.
1594:             *      Handles the following syntax:
1595:             *
1596:             *        ensemble <ensName> {
1597:             *            part <partName> <args> <body>
1598:             *        }
1599:             *
1600:             *      Adds a new part called <partName> to the ensemble.  If a
1601:             *      part already exists with that name, it is an error.  The
1602:             *      new part is handled just like an ordinary Tcl proc, with
1603:             *      a list of <args> and a <body> of code to execute.
1604:             *
1605:             * Results:
1606:             *      If anything goes wrong, this procedure raises a TclException.
1607:             *
1608:             * Side effects:
1609:             *      None.
1610:             *
1611:             *----------------------------------------------------------------------
1612:             */
1613:
1614:            static class EnsPartCmd implements  Command {
1615:                EnsembleParser ensParser;
1616:
1617:                EnsPartCmd(EnsembleParser ensParser) {
1618:                    this .ensParser = ensParser;
1619:                }
1620:
1621:                public void cmdProc(Interp interp, // Current interp.
1622:                        TclObject[] objv) // Args passed to the command.
1623:                        throws TclException {
1624:                    EnsembleParser ensInfo = ensParser;
1625:                    Ensemble ensData = ensInfo.ensData;
1626:
1627:                    boolean varArgs, space;
1628:                    String partName, usage;
1629:                    Procedure proc;
1630:                    WrappedCommand wcmd;
1631:                    //CompiledLocal *localPtr;
1632:                    EnsemblePart ensPart;
1633:                    StringBuffer buffer;
1634:
1635:                    if (objv.length != 4) {
1636:                        throw new TclException(interp,
1637:                                "wrong # args: should be \""
1638:                                        + objv[0].toString()
1639:                                        + " name args body\"");
1640:                    }
1641:
1642:                    //  Create a Tcl-style proc definition using the specified args
1643:                    //  and body.  This is not a proc in the usual sense.  It belongs
1644:                    //  to the namespace that contains the ensemble, but it is
1645:                    //  accessed through the ensemble, not through a Tcl command.
1646:
1647:                    partName = objv[1].toString();
1648:                    wcmd = ensData.wcmd;
1649:
1650:                    proc = ItclAccess.newProcedure(interp, wcmd.ns, partName,
1651:                            objv[2], objv[3], "unknown", 0);
1652:
1653:                    //  Deduce the usage information from the argument list.
1654:                    //  We'll register this when we create the part, in a moment.
1655:
1656:                    buffer = new StringBuffer();
1657:                    varArgs = false;
1658:                    space = false;
1659:
1660:                    TclObject[][] argList = ItclAccess.getArgList(proc);
1661:
1662:                    for (int i = 0; i < argList.length; i++) {
1663:                        TclObject vname = argList[i][0];
1664:                        TclObject def = argList[i][1];
1665:
1666:                        varArgs = false;
1667:                        if (vname.toString().equals("args")) {
1668:                            varArgs = true;
1669:                        } else if (def != null) {
1670:                            if (space) {
1671:                                buffer.append(" ");
1672:                            }
1673:                            buffer.append("?");
1674:                            buffer.append(vname);
1675:                            buffer.append("?");
1676:                            space = true;
1677:                        } else {
1678:                            if (space) {
1679:                                buffer.append(" ");
1680:                            }
1681:                            buffer.append(vname);
1682:                            space = true;
1683:                        }
1684:                    }
1685:                    if (varArgs) {
1686:                        if (space) {
1687:                            buffer.append(" ");
1688:                        }
1689:                        buffer.append("?arg arg ...?");
1690:                    }
1691:
1692:                    usage = buffer.toString();
1693:
1694:                    // Create a new part within the ensemble.  If successful,
1695:                    // plug the command token into the proc; we'll need it later.
1696:
1697:                    ensPart = AddEnsemblePart(interp, ensData, partName, usage,
1698:                            proc);
1699:
1700:                    ItclAccess.setWrappedCommand(proc, ensPart.wcmd);
1701:                }
1702:            } // end class EnsPartCmd
1703:
1704:            /*
1705:             *----------------------------------------------------------------------
1706:             *
1707:             * Itcl_EnsembleErrorCmd -> Ensemble.EnsembleErrorCmd
1708:             *
1709:             *      Invoked when the user tries to access an unknown part for
1710:             *      an ensemble.  Acts as the default handler for the "@error"
1711:             *      part.  Generates an error message like:
1712:             *
1713:             *          bad option "foo": should be one of...
1714:             *            info args procname
1715:             *            info body procname
1716:             *            info cmdcount
1717:             *            ...
1718:             *
1719:             * Results:
1720:             *      None.
1721:             *
1722:             * Side effects:
1723:             *      Raises a TclException with an error message.
1724:             *
1725:             *----------------------------------------------------------------------
1726:             */
1727:
1728:            static void EnsembleErrorCmd(Ensemble ensData, // like client data in C version
1729:                    Interp interp, TclObject[] objv, int skip)
1730:                    throws TclException {
1731:                String cmdName;
1732:                StringBuffer buffer = new StringBuffer(64);
1733:
1734:                cmdName = objv[skip].toString();
1735:
1736:                buffer.append("bad option \"");
1737:                buffer.append(cmdName);
1738:                buffer.append("\": should be one of...\n");
1739:                GetEnsembleUsage(ensData, buffer);
1740:
1741:                throw new TclException(interp, buffer.toString());
1742:            }
1743:
1744:            /*
1745:             *----------------------------------------------------------------------
1746:             *
1747:             * FreeEnsInvocInternalRep -> Ensemble.FreeEnsInvocInternalRep
1748:             *
1749:             *      Frees the resources associated with an ensembleInvoc object's
1750:             *      internal representation.
1751:             *
1752:             * Results:
1753:             *      None.
1754:             *
1755:             * Side effects:
1756:             *      Decrements the ref count of the two objects referenced by
1757:             *      this object.  If there are no more uses, this will free
1758:             *      the other objects.
1759:             *
1760:             *----------------------------------------------------------------------
1761:             */
1762:
1763:            static void FreeEnsInvocInternalRep(ItclEnsInvoc obj) {
1764:                TclObject prevArgObj = obj.chainObj;
1765:
1766:                if (prevArgObj != null) {
1767:                    prevArgObj.release();
1768:                }
1769:            }
1770:
1771:            /*
1772:             *----------------------------------------------------------------------
1773:             *
1774:             * DupEnsInvocInternalRep -> Ensemble.DupEnsInvocInternalRep
1775:             *
1776:             *      Duplicate the given internal representation of an ensembleInvoc.
1777:             *
1778:             *      This shouldn't be called.  Normally, a temporary ensembleInvoc
1779:             *      object is created while an ensemble call is in progress.
1780:             *      This object may be converted to string form if an error occurs.
1781:             *      It does not stay around long, and there is no reason for it
1782:             *      to be duplicated.
1783:             *
1784:             * Results:
1785:             *      None.
1786:             *
1787:             * Side effects:
1788:             *      returns copy of internal rep with duplicates of the objects
1789:             *      pointed to by src's internal rep.
1790:             *
1791:             *----------------------------------------------------------------------
1792:             */
1793:
1794:            static InternalRep DupEnsInvocInternalRep(ItclEnsInvoc obj) // internal rep to copy.
1795:
1796:            {
1797:                ItclEnsInvoc dup = new ItclEnsInvoc();
1798:                dup.ensPart = obj.ensPart;
1799:                dup.chainObj = obj.chainObj;
1800:
1801:                if (dup.chainObj != null) {
1802:                    dup.chainObj.preserve();
1803:                }
1804:
1805:                return dup;
1806:            }
1807:
1808:            /*
1809:             *----------------------------------------------------------------------
1810:             *
1811:             * SetEnsInvocFromAny -> Ensemble.SetEnsInvocFromAny
1812:             *
1813:             *      Generates the internal representation for an ensembleInvoc
1814:             *      object.  This conversion really shouldn't take place.
1815:             *      Normally, a temporary ensembleInvoc object is created while
1816:             *      an ensemble call is in progress.  This object may be converted
1817:             *      to string form if an error occurs.  But there is no reason
1818:             *      for any other object to be converted to ensembleInvoc form.
1819:             *
1820:             * Results:
1821:             *      None.
1822:             *
1823:             * Side effects:
1824:             *      None.
1825:             *----------------------------------------------------------------------
1826:             */
1827:
1828:            static void SetEnsInvocFromAny(Interp interp, // Determines the context for
1829:                    // name resolution
1830:                    TclObject obj) // The object to convert
1831:                    throws TclException {
1832:                // unused
1833:            }
1834:
1835:            /*
1836:             *----------------------------------------------------------------------
1837:             *
1838:             * UpdateStringOfEnsInvoc -> Ensemble.UpdateStringOfEnsInvoc
1839:             *
1840:             *      Updates the string representation for an ensembleInvoc object.
1841:             *      This is called when an error occurs in an ensemble part, when
1842:             *      the code tries to print objv[0] as the command name.  This
1843:             *      code automatically chains together all of the names leading
1844:             *      to the ensemble part, so the error message references the
1845:             *      entire command, not just the part name.
1846:             *
1847:             * Results:
1848:             *      Returns the full command name for the ensemble part.
1849:             *
1850:             *----------------------------------------------------------------------
1851:             */
1852:
1853:            static String UpdateStringOfEnsInvoc(ItclEnsInvoc obj) // internal rep
1854:            {
1855:                EnsemblePart ensPart = obj.ensPart;
1856:                TclObject chainObj = obj.chainObj;
1857:
1858:                StringBuffer buffer = new StringBuffer(64);
1859:                int length;
1860:                String name;
1861:
1862:                //  Get the string representation for the previous argument.
1863:                //  This will force each ensembleInvoc argument up the line
1864:                //  to get its string representation.  So we will get the
1865:                //  original command name, followed by the sub-ensemble, and
1866:                //  the next sub-ensemble, and so on.  Then add the part
1867:                //  name from the ensPart argument.
1868:
1869:                if (chainObj != null) {
1870:                    name = chainObj.toString();
1871:                    buffer.append(name);
1872:                }
1873:
1874:                if (ensPart != null) {
1875:                    Util.AppendElement(buffer, ensPart.name);
1876:                }
1877:
1878:                return buffer.toString();
1879:            }
1880:
1881:        } // end class Ensemble
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.