A Partial Implementation of a Modular System for Software Safety Analysis by Sara C. Pickett Submitted to the Department of Electrical Engineering and Computer Science in partial fulfillment of the requirements for the degrees of Bachelor of Science in Computer Science and Master of Engineering in Electrical Engineering and Computer Science at the MASSACHUSETTS INSTITUTE OF TECHNOLOGY May 2000 @ Sara C. Pickett, MM. All rights reserved. The author hereby grants to MIT permission to reproduce and distribute publicly paper and electronic copies of this thesis document in whole or in part. ... Author .. .. . .................................... Department of Electrical Engineering and Computer Science May 22, 2000 /I Certified by.. /7 Nancy G. Leveson Professor Thesis Supervisor Accepted by ........ Arthur C. Smith Chairman, Department Committee on Graduate Theses MASSACHUSETT S INSTITUTE OF TECHN XLOGY ENG JUL 2 7 2000 LIBRA RIES A Partial Implementation of a Modular System for Software Safety Analysis by Sara C. Pickett Submitted to the Department of Electrical Engineering and Computer Science on May 22, 2000, in partial fulfillment of the requirements for the degrees of Bachelor of Science in Computer Science and Master of Engineering in Electrical Engineering and Computer Science Abstract In this thesis, I describe how I designed and partially implemented an editor for SpecTRMRL documents, to be used as part of a cross-platform, extensible system for performing software safety analysis. This editor, written using Java and Swing, allows for the creation of links between areas of the document, tracks variable definition and usage, and provides methods for saving and loading SpecTRM-RL documents. The editor was designed to later be used as part of a much larger system which would allow analysis and execution of the SpecTRM-RL document. Thesis Supervisor: Nancy G. Leveson Title: Professor 2 Acknowledgments This work was partially supported by NSF Grant CCR-9996265. Sun, Sun Microsystems, the Sun Logo, Solaris, Java, JavaBeans, JDK, JFC, Swing, and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. Microsoft Windows and Microsoft Word are either registered trademarks or trademarks of Microsoft Corporation in the United States and/or other countries. Macintosh, MacOS, and iMac are either registered trademarks or trademarks of Apple Computer, Inc., registered in the U.S. and other countries. Linux is a registered trademark of Linus Torvalds. 3 Contents 1 Introduction 1.1 1.2 2 3 4 7 M otivations . . . . . . . . . . . . . . . . . . 7 1.1.1 Motivations for Safety Analysis . . . 7 1.1.2 Motivations for Intent Specifications 8 1.1.3 Motivations for SpecTRM-RL . . . . 9 Background . . . . . . . . . . . . . . . . . . 9 Design Criteria 10 2.1 Extensibility and Modularity . . . . . . . . 10 2.2 Cross-Platform Functionality . . . . . . . . 11 2.3 Requirements Imposed by SpecTRM-RL . . 11 Implementation 13 3.1 Choosing JavaTM for the Programming Language . . . . . . . . . . . . . . . 13 3.1.1 13 Choosing Java Version 1.1 . . . . . . . . . . . . . . . . . . . . . . . . 3.2 Quick SpecTRM-RL Overview . . . . . . . . . . . . . . . . . . . . . . . . . 13 3.3 Discussion of Implementation Details . . . . . . . . . . . . . . . . . . . . . . 14 3.3.1 Context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 3.3.2 Location . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 3.3.3 The Status of Links . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 3.3.4 Component-Specific Data and Functionality . . . . . . . . . . . . . . 16 3.3.5 Saving and Loading Files . . . . . . . . . . . . . . . . . . . . . . . . 17 Conclusions 21 4.1 21 Discussion of the Future Directions . . . . . . . . . . . . . . . . . . . . . . . 4 4.2 Contributions . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . A Screenshots 21 23 A.1 Details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 A.2 Example Screenshots . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 B Code 26 B.1 BooleanAtom.java . ... . .. ... .. .. . .. .. . .. .. . .. . .. .. 26 B.2 BooleanAtomEd.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 B.3 SimpleBooleanDocument.java . . . . . . . . . . . . . . . . . . . . . . . . . . 29 BA BooleanExpr.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 B.5 BooleanExprEd.java ... . .. ... .. .. .. .. . .. . .. . .. . .. .. 31 B.6 SpecExprParsing.java .. . .. ... .. .. . .. .. .. . .. . .. . .. .. 34 B.7 TestStuff.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 B.8 Output CommandComp.j ava . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 B.9 SpecTextBox.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 B.10 BooleanTableComp.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 B.11 BooleanTableModel.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 B.12 BooleanTableColumnModel.java .. . ... . .. .. .. . .. . .. . .. .. 53 . .. .. .. ... .. .. . ... . .. . .. . .. .. .. . 54 B.14 SpecContext.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 B.15 SpecVar.java .. .. .. .. .. ... .. .. .. .. . .. . .. . .. .. .. . 58 B.16 AlreadyDefinedException.java . . . . . . . . . . . . . . . . . . . . . . . . . . 60 B.17 StillInUseException.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 B.18 util.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 B.19 BasicWindowMonitor.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 B.13 SpecLocationjava 5 List of Figures 1-1 The structure of an intent specification, from [5]. 2-1 Example of an Output Command Specification, showing fields, tables, variable usage, and links, from [4]. . . . . . . . . . A-1 A blank Output Command Specification window, showing fields and tables. 8 12 24 A-2 The same Output Command Specification window, this time filled in with the same information as in 2-1. . . . . . . . . . . . . . . . . . . . . . . . . . 6 25 Chapter 1 Introduction In this thesis, I will discuss the implementation of an editor for Specification Tools and Requirements Methodology Requirements Language (SpecTRM-RL) which must work within the framework of a system for documenting intent specifications which are used for doing analyses of the safety of large scale software projects. 1.1 1.1.1 Motivations Motivations for Safety Analysis Most computer programmers work on non-safety-critical systems. A bug in the specification or mis-implementation of the specification may result in some lost money for their employer, lost time for their research group, or aggravation for the end user, but typically nothing truly horrendous will occur. But in safety-critical systems, a serious flaw in a critical computer program may result in a loss of life, a catastrophic environmental disaster, or the loss of missions of dollars and years of research effort. Airplane autopilots, satellite controllers, and nuclear power plant automation are all examples of safety-critical systems involving computers. Traditional programming validation techniques include testing, code reviews, and maybe a little proof-writing for any particularly complex algorithms. But none of these techniques are really up to dealing with large-scale safety critical systems. 7 Decomposition Refinement Environment Operator System Components System Purpose System Principles Intent Blackbox Behavior I Design Representation I __________________________I Code (Physical Representation) Figure 1-1: The structure of an intent specification, from [5]. 1.1.2 Motivations for Intent Specifications Besides hardware failure of the machine(s) the software is running on, there are really only two types of errors software can have. The software may meet its requirements specification, but the behavior specified may not be what is desired from a system safety perspective, or the software as implemented may not meet its specification [3, p. 157]. Common opinion is that the first type of error is much more common than the second, but regardless, it's clear that the specification of software requirements is central to the issue of software safety. Because of this, much work has gone into exploring ideas about how software specifications should be written so as to promote safety. One of these ideas is Prof. Nancy Leveson's "intent specifications" [5]. This format for writing specifications supports system engineering by providing a way to record decisions at every step of design. To do this, it has a structure with several dimensions of layers of specifications - one dimension for refinement, another for decomposition, and a third for levels of intent, with linkages between and across layers to help record why decisions were made as they were. The intent levels are System Purpose, System Principles, Blackbox Behavior, Design Representation, and Code (effectively the Physical Representation). 8 1.1.3 Motivations for SpecTRM-RL For the blackbox behavior level, some format for describing the blackbox behavior of the system needs to be used. Although this could be merely a textual description of the desired behavior, that wouldn't be precise enough for most purposes. On the other hand, the specification needs to be readable, or no one will read it to check its correctness. One language, SpecTRM-RL (Specifications Tools and Requirements Methodology-Requirements Language) is designed to be both easy to use without extensive training and precise enough to be formally analyzable by automated tools.[5] This language was based upon RSML (Requirements State Machine Language)[7], and should be precise enough to be used in completeness analysis[6], as well as other forms of analysis. 1.2 Background As far as I have been able to determine, no previous attempts to write a SpecTRM-RL editor have been made. An alpha version of an editor for RSML, the predecessor to SpecTRM-RL, exists, named Nimbus. This editor relied heavily upon Microsoft WordTM templates and macros, and is dependent upon the Microsoft WindowsTM platform in general. 9 Chapter 2 Design Criteria Several design criteria for the intent specification system, and the SpecTRM-RL editor which will be used within it, were identified right from the start. The main goal was for the system be useful for system safety analysis and yet not easily made obsolete. The system must be extensible, to allow for new forms of analysis to be performed, new document formats to be understood, and other modifications to be made easily. The system must also be usable on several different computing platforms, rather than relying on a single operating system or closely related set of systems. 2.1 Extensibility and Modularity Because no computer program can be considered complete and whole in its first version, the intent specification system must be extensible. Both the addition of new functionality and the routine maintenance of existing code need to be made as easy as possible. Future changes might include new forms of analysis (based on the SpecTRM-RL itself, or on data derived from it), new document formats based on the need of newer versions to include more information, or new methods of loading documents (based on retrieving them from the Internet or from a revision control repository). The overall intent specification system's framework needs to be designed with extensibility and modularity in mind[1]. The SpecTRM-RL editor must support the modularity of the overall framework, both by being written in as readable and modular a way as possible, and by conforming to any requirements specified by the framework. However, since the framework wasn't finalized before this version of the editor was written, it probably does not entirely meet that 10 requirement. 2.2 Cross-Platform Functionality It is also extremely important that the SpecTRM-RL editor be able to function on multiple computing platforms. This is not only to allow more users to use the intent specification system on their platform of choice, but also to prevent the system from being made obsolete or unusable by the obsolescence or incompatible modification of the computing system it depends on. Besides running on more than one type of hardware system and more than one operating system, the programming language used to implement the system and any other software components (such as libraries or communication protocols) that the system depends on must be popular enough that continued availability and support seems likely. 2.3 Requirements Imposed by SpecTRM-RL There are also some requirements imposed on the intent specification system and the SpecTRM-RL editor by the format of the SpecTRM-RL language itself. The system must be able to load and save SpecTRM-RL documents, but with few, if any, existing SpecTRMRL editors, backwards compatibility is not too much of an issue. However, clearly the editor needs to allow the user to enter the information which is part of SpecTRM-RL, as described in [4]. This information includes sections for output commands, operating modes, state values, input values, control inputs, display outputs, and macros. In each section, values for various fields have to be filled in, and tables representing Boolean expressions have to be created. Also, the user needs to be able to create links from one part of the SpecTRMRL document to another (or to a location in the larger intent specification document). In addition to user created links, links should be automatically created between a variable's definition and its usages. 11 Output Command DOI-Power-On Destination: DOI Acceptable Values: (high) Initiation Delay: 0 milliseconds Completion Deadline: 50 milliseconds Exception-Handling: Feedback Information: Variables: DOI-status-signal Values: high (on) Relationship: Should be on if ASW sent signal to turn on time (latency): 2 seconds Max. time: 4 seconds Exception Handling: DO-Status changed to Fault-Detected Min. Reversed By: Turned off by some other component or components. Do not know which ones. Comments: References: 4.7 4 2.4.5 CONTENTS = discrete signal on line PWR set to high TRIGGERING CONDITION Operating Mode State Values Operational Not Inhibited T DOI-Status = On F Below-threshhold T Prev(Altitude) = At-or-above-threshold T Altitude = Figure 4: Example of an Output Specification Figure 2-1: Example of an Output Command Specification, showing fields, tables, variable usage, and links, from [4]. 12 Chapter 3 Implementation Choosing JavaTM for the Programming Language 3.1 Before any actual implementation could take place, I had to chose a programming language. After consulting with some other people, I chose Sun's JavaTM because it had the important property of being available on several platforms (SolarisTM, Windows, LinuxTMI and MacOSTM), and also because the SwingTM library[9] (part of the Java Foundation Classes[11] suite) is considered to be very easy to use for creating GUIs. 3.1.1 Choosing Java Version 1.1 The editor was written using only methods and classes from JDKTM 1.1 110], even though JDK 1.3 is the current version, because version 1.1 did not lack any of the functionality needed and is the newest version available for some platforms (most notably MacOS). The implementation of the outer intent specification editing framework may have to be written using the relatively well-supported JDK 1.2, however. Since Swing is included with every installation of JDK 1.2, but not with JDK 1.1, JDK 1.1 users will have to also have separately installed a copy of the Swing libraries. Once that is done, however, the editor works identically under both version. 3.2 Quick SpecTRM-RL Overview From the implementers point of view, the format of a SpecTRM-RL document is fairly straight-forward. For each section of the SpecTRM-RL document (such as the output 13 commands section), there will be some number of definitions which fit into that section. Each definition will follow the template provided by SpecTRM-RL for that category. Each template includes fields such as "Source", "Type", "Possible Values", or "Units" into which information must be filled by the user. As well as text, these fields may contain links to other portions of the document. Besides the text fields, the actual definition is given in terms of a set of AND/OR tables which represent disjunctive normal form (DNF) compound Boolean expressions. These tables hold simple Boolean expressions (such as "Altitude = Below-threshold") in their first column, and Boolean constants in their other columns. 3.3 Discussion of Implementation Details Most of the implementation was relatively straight-forward. Each component of the SpecTRMRL document had some amount of component-specific data (the contents of a text field or the contents of a table), but every component also had to know its current location and the "context" within which it was created. 3.3.1 Context Each component knows the "context" in which it was created - that is, which variables, states, and macros have been defined and where they are used. This SpecContext class is stored as a hash table, keyed by variable/state/macro name, which is used to store a SpecVar which holds the name, type, location of definition, and a list of usage locations. Usages can be added and removed for each variable, and variables can be added to the context when they're defined, and removed when their definition is removed, but not until all usages have been removed. 3.3.2 Location The initial goal was to create some sort of reasonably sophisticated object which could be used to record the location of objects (such as variable definitions and usages), so that the location could be recorded and later looked up, possibly by clicking on hyperlinks to go to that location. In the existing implementation, every component knows its SpecLocation, and knows how to modify that location object to obtain the locations of any other objects it contains. 14 It passes the child's location in to it when the child is created, and since that child will also know how to modify that location to give locations to its children, the locations are perpetuated. These location objects are fine for being stored and retrieved to keep track of variable usage, but a mechanism for scrolling to a given Swing Component based on a location object has not been implemented. In fact, the SpecLocations are basically strings. A table's location might be "the table in the macro definition panel for Altitude-is-high", and every table may know that in order to get the location of their fourth row, combine "row #4 of" onto the front of their own location, to produce the location "row #4 of the table in the macro definition panel for Altitude-is-high". This implementation, while unsophisticated, is at least nicely human readable. 3.3.3 The Status of Links The ability to create references or hyperlinks to other parts of the SpecTRM-RL document (and to other external documents) has not yet been implemented, since it proved to be more complex than originally realized. At first it seemed that URLs would be ideal for this purpose - URLs seemed ideal for pointing to arbitrary files available on the Internet, URLs starting with f ile: // could be used to link to other local files, and some sort of TAG scheme could be used to link within SpecTRM-RL files, just as they're commonly used to label parts of HTML files for linking. However, this scheme has several problems, mostly related to tracking changes. The first problem has to do with versioning. When you link to a file somewhere on the Internet, you have no way of knowing when that file if that file was changed to say something totally different than it did when you last viewed it. This is mostly a nuisance for web page designers. However, for a system safety documentation tool, this seemed possibly dangerous. A better system would have some concept of versioning, so that you would not be automatically linked into the newest version of another document without reviewing it, but at the same time you would be informed of modifications to other documents, so that you could choose to update your links if appropriate. Although some system like this could be based around URLs, it would require a lot of imposed structure that standard URLs don't have. As well as versioning, there's also the problem that there is no way of guaranteeing 15 that the remote document will continue to exist. Or that it will continue to exist in the same location (it could be moved). One can imagine working around these problems by always going through a central authority before allowing document deletions to check if other documents depend on your document, or always requesting the canonical location of a document before attempting to find it. Some scheme to map CVS[2} repository files to URLs would seem most appropriate, but at that point, why use URLs at all? But at the same time, CVS doesn't satisfy all the requirements either. Intra-document links are a much easier problem, since consistency checking within a single SpecTRM-RL document, or even a few associated documents all on the local disk, could be controlled by the SpecTRM-RL editor or the larger intent specification system. However, although the existing editor keeps track of locations for every component, and the locations of variable definitions and usages, it does not implement the hyperlinks connecting the two. The current editor keeps track of these "links", but they do not appear as hyperlinks to the user, yet. 3.3.4 Component-Specific Data and Functionality Fields For each field in the template for a category, the text is entered into a JTextArea. As well allowing the user to enter text and saving it as part of the document, each field does a minimal amount of parsing on its contents to extract a list of variables that were used in that field. This list can be used to construct links between variable definitions and variable usages. More extensive parsing and validation of the text might be possible, but most of the fields seemed to be free-form, and so more extensive parsing was left to be implemented by outer layers of analysis tools or future versions of the editor in order to avoid excessively limiting the user in what they could enter and document. Tables Each definition table is a JTable to which rows and columns can be added as needed. The leftmost column hold a Boolean expression and is effectively a JTextField, which can hold a single line of text. That column is parsed enough to detect variables and logically link 16 variable usages to their definitions. A future version of the editor could do further parsing, such as checking whether the text is a valid Boolean expression, but the current version does not, as that functionality has been left to an outer layer syntax checking tool of some sort. The other columns in the table are only allowed to hold simple Boolean values - true, false, or don't care. Because of this, those columns are JTextField's which validate their inputs and only allow "T", "F", or "." to be entered. Parsing The contents of both the text fields and the Boolean expression columns have to be parsed in order to find variables. The Boolean expression text is broken into "words" at whitespace, while the text field parsing knows to also remove commas and periods from words. Then each word is looked up to see if it's a defined variable/macro/state. If the word contains an underscore, the portion of the word before the underscore is used as the name to be looked up. (This is because the plan was to eventually have the first letter of the variable's type be used as a subscript to the name wherever it appeared - for example foobarm whenever the macro foobar was referred to. In the tradition of IBLTX, this would be entered by the user as f oobarim.) Once some of the words are determined to be variables, those the editor updates the recorded usages for those variables. Eventually, it should also highlight the variables in another color, and possibly add hypertext links to the variable definitions, but neither of those have yet been implemented. 3.3.5 Saving and Loading Files Using Serialization At first, saving and loading SpecTRM-RL documents seemed almost trivial. Java pro- vides a process called "serialization" for automatically saving and loading objects to and from disk. If a class is declared as implementing the Serializable interface, then a call to java.io.ObjectOutputStream.writeObject( ourObject ) is enough to save all in- formation about ourObject to disk, including all non-transient member variables and all information from superclasses. This initially seemed ideal for saving SpecTRM-RL documents. Merely serialize the 17 top-level object which represents a "document", and all information about it would be automatically saved. Later create a new object using de-serialization, and the document would be recreated. The problem with this is that many of the objects used in a SpecTRM-RL document are Swing objects or extensions (subtypes) of Swing objects, and the serialization format for Swing objects has not yet be finalized, according to the many warnings in the documentation[9]. This means that any documents saved by the editor running under current versions of Java and current version of Swing would be unreadable to the editor running under a future version of Swing. Also, Swing's JTables, which were used to implement the DNF truth tables in SpecTRMRL, are not serializable, and so serialization could not be used without intermediate glue of some sort being implemented. While not extremely difficult, this provided another reason not to just use Java's serialization. Overriding Serialization At first the solution seemed simple - override the default implementation of writeObj ect 0 and readObject to save the relevant information about Swing-derived objects, including instance variables and relevant information about the supertype in writeObject 0 and then load the instance variables back in and reconstruct any desired information about the supertype (such as which Components it contained, and how they were laid out) in readObject 0. That's what Java lets you do if the supertype isn't serializable at all, so it seemed reasonable to think that if you proceeded as if the supertype weren't serializable, Java would let you do the serialization yourself. Unfortunately, this turned out not to be the case. For example, if you don't save any information about the supertype in your writeObject 0 method, that information is still automatically recreated when you run readObject(). So there seemed to be no way of serializing a class which extends a Swing class without also automatically serializing the Swing class in a soon-to-be-incompatible way. Actual Implementation Since it was not possible to simply override the writeObject () and readObject () methods to avoid serializing the Swing objects, I had to write my own methods for saving and loading all of the classes which were derived from Swing objects. The method for saving 18 was saveSelf ( java.io.ObjectOutputStream ) and the method for loading was a constructor taking a java. io. ObjectInputStream as its only argument. These methods had to explicitly save all instance variables and any info from the supertype that needed saving. Any information from the supertype that could be recreated (such as layout and positioning of the components) was discarded when saving and reconstructed when loading. Any Swing-derived instance variables were saved using their saveSelf 0 methods, while any non-Swing information was saved using just writeObject 0 for serialization. The first thing to be careful about when doing the serialization by hand is that all object must be read back in the same order that they were written. Otherwise, the saved objects may not be assigned to the correct member variables, which may cause ClassCastExceptions on loading, and will definitely cause data corruption. The second thing to be careful about has to do with the saving of container objects, such as Vectors. To save a homogeneous (one whose elements are all of the same type) Vector, it is sufficient to first save its length as an Integer using writeObj ect 0, and then call saveSelf () on each of the elements. To load, first perform a readObject () to find out the length, and then call the element type's constructor the appropriate number of times. However, when the vector is non-homogeneous and it contains some Swing-derived elements, things become tricky. We can't just read the length and then call the constructor an appropriate number of times, because there is no way of knowing which constructor to call. readObj ect 0 is much more suitable in this regard, since it returns an Object for which the caller can then check the type. Also, if the Vector contains some Swing-derived elements and some non-Swing elements, it's not clear for any given element whether readObj ect 0 or a constructor should be called. For recursive Vectors of Vectors, things become even more complicated, since the Integer marking the beginning of the Vector cannot be distinguished from an Integer which is simply an element of the parent Vector. Complications such as these make one with for the ability to use regular Java serialization, which automatically marks the type and size of everything in such a way as to make the objects reconstructible with just a call to readObj ect (). However, the non-homogeneous recursive Vector of Vectors problem only came up in one place in the code - when saving the SpecTextBoxes for the various category panels (OutputCommandComp, etc.). There, I needed to save a Vector whose elements were each either a SpecTextBox or a sub-Vector of SpecTextBoxes and sub-Vectors. For that case, the easiest thing to do was to write 19 a Boolean flag out before each element, indicating which it was, so that the appropriate method or constructor could be called when the data was read back in. Although not elegant, this implementation of saving and loading works, and was relatively easy to implement. Some other methods were considered, but were rejected as being too complicated to implement in a limited amount of time. Other Possibilities Both Java's standard object serialization and the solution that was actually implemented have one serious undesirable property - the save files they produce are not human-readable and are effectively readable only by Java programs. A more attractive solution in this regard would be the use of XML[12] - a text-based universal format for structured documents and data. The editor could either include code to produce and interpret XML text to represent each component object, or could use some automated scheme for producing the XML. There is, in fact, a proposal to allow Java serialization to automatically produce XML documents from JavaBeansTM (and every Swing component is a JavaBean), but at this point it is only a proposal. [8] Mainly due to time constraints, neither of these options were implemented, but they seem very interesting and deserve further exploration. 20 Chapter 4 Conclusions 4.1 Discussion of the Future Directions Although the basic framework for the SpecTRM-RL editor is there, there are several obvious steps that could be taken to improve it. Improvements to the saved document format (possibly using XML), the addition of the ability to print SpecTRM-RL documents, and better feedback within the editor regarding variable usage and definition (either using text coloring or highlighting, or by displaying the information in a separate window) would all be good starts. Besides improvements to the SpecTRM-RL editor, once a general intent specification framework and editor are written, there will be plenty of work necessary in order to integrate them with the SpecTRM-RL editor. As well as being invoked by the outer intent specification editor, and referring links to the outer editor, the SpecTRM-RL editor will have to communicate with the SpecTRM-RL / RSML simulator, as well as with programs of various sorts which would like to analyze the blackbox specification in various ways. 4.2 Contributions This thesis contributes an actual implementation of a SpecTRM-RL editor that is usable, although lacking in some features. It also documents some of the issues encountered while exploring ways to implement the editor. Although Swing made it relatively easy to implement a GUI, it's still by no means trivial. And although there are a lot of built-in or publicly available libraries for doing almost what was needed (URLs for inter-document 21 links, Serialization for document saving), adapting them to suit what was actually needed and combining them all into the same piece of software proved to be rather challenging. 22 Appendix A Screenshots A.1 Details These screen shots of the implementation were taken on an Apple iMacTM and on a SunTM workstation. The MacintoshTM was installed with MacOS 8.6 with Macintosh Runtime for Java (MRJ) version 2.2, which implements Sun's JDK 1.1.8 specification, and with Swing 1.1.1 (officially known as Java Foundation Classes (JFC) 1.1). On the Sun workstation, JDK 1.2.2 was used. A.2 Example Screenshots Figure A-1 shows a blank Output Command Specification window, while A-2 shows it filled in. 23 Figure A-1: A blank Output Command Specification window, showing fields and tables. 24 Figure A-2: The same Output Command Specification window, this time filled in with the same information as in 2-1. 25 Appendix B Code This code can also be found in http://web.mit. edu/~sarac/Public/SpecTRM-RL-editor/SpecTRM-RL-editor-vl.0. B.1 tar. gz BooleanAtom.java import java.io.Serializable; public class BooleanAtom implements Serializable private String value = ""; public BooleanAtomO { { value = } 10 public BooleanAtom(String s) { if ((s == null) |I (s.equals("")) II (s.equals(" "))) { } s = s.toUpperCase(; if (!((s.equals("T")) II (s.equals("F") (s.equals(". "))))) { throw new IllegalArgumentException("Can't use \"" + s + "\" to construct a BooleanAtom."); 20 } value = s; } public String toString() { return(value); } 30 26 } BooleanAtomEd.java B.2 import java.awt.Component; import java.util.*; import javax.swing.table.*; import javax.swing.*; import javax.swing.event.*; public class BooleanAtomEd extends JTextField implements TableCellEditor protected transient Vector listeners; protected transient BooleanAtom originalValue; { 10 public BooleanAtomEd() { // construct a new JTextField with our document that // disallows all chars except T/F/" " super(new SimpleBooleanDocument(), null, 2); // changing the alignment when editting is useful for debugging //setHorizontalAlignment(JTextField.CENTER); // Can only ever be one character wide, so set // that as the true preferred width now. 20 setText("F"); setPreferredSize(getPreferredSize(); // Canonicalizeformat setText(new BooleanAtomo.toStringo); // // // // // Scroll back to the left This isn't actually useful here, since the constructor is only called once for the entire table. But there are 30 a lot of issues here with tabbing through the table to edit cells and where the insertion point ends up - FIXME setScrollOffset(O); listeners = new Vector0; } public Component getTableCellEditorComponent(JTable table, 40 Object value, boolean isSelected, int row, int column) { if (value == null) { setText(""); return(this); } 50 if (value instanceof BooleanAtom) { setText(((BooleanAtom)value).toString(); } else { 27 throw new IllegalArgumentException(" BooleanAt omEd can only be" + " used on BooleanAtom's."); } table.setRowSelectionInterval(row, row); table.setColumnSelectionInterval(column, column); originalValue = new BooleanAtom(getText(); 60 7* System. out.println( "BooleanAtomEd.getTableCellEditorComponentcalled" + with arguements:\n" + "value: \"" + value + "\"\n" + "isSelected: \"" + isSelected + "\"\n" + "row: \"" + row + "\"\n" + "column: \"" + column + "\"\n" + 70 */ return(this); } public void cancelCellEditing() { fireEditingCanceledo; } 80 public Object getCellEditorValue() { // Somewhere we should add a check here to only create a new // BooleanAtom if the text changed (otherwise use the /7 old one)? Is this the right place??? - FIXME 7* Value called when:\n" + System. out.println("BooleanAtomEd.getCellEditor "old value was: \"" + originalValue + "\'\n" + "new value is: \"" + getText() + "\'n" + 90 */ return new BooleanAtom(getTextO); I public boolean isCellEditable(EventObject eo) { public boolean shouldSelectCell(EventObject eo) return true; { } 100 return true; } public boolean stopCellEditing() { fireEditingStopped(; return true; } 110 public void addCellEditorListener(CellEditorListener cel) listeners.addElement(cel); 28 { I public void removeCellEditorListener(CellEditorListener cel) { listeners.removeElement(cel); } 120 protected void fireEditingCanceled() setText(originalValue.toString()); { ChangeEvent ce = new ChangeEvent(this); for (int i = listeners.sizeo; i >= 0; i--) { ((CellEditorListener)listeners.elementAt(i)).editingCanceled(ce); } } 130 protected void fireEditingStopped() { ChangeEvent ce = new ChangeEvent(this); for (int i = listeners.size() - 1; i >= 0; i--) { ((CellEditorListener)listeners.elementAt(i)).editingStopped(ce); } } } B.3 SimpleBooleanDocument.java import java.awt.Toolkit; import javax.swing.*; import javax.swing.text.*; public class SimpleBooleanDocument extends PlainDocument public SimpleBooleanDocument() super(; { { } 10 public void insertString(int offset, String str, AttributeSet a) throws BadLocationException { str = str.toUpperCaseo; if ((getLength() + str.length() > 1) 11 !((str.equals("T")) 1| (str.equals("F")) 11 (str.equals(" ")) 11 (str.equals(" I. )))) { Toolkit.getDefaultToolkit().beep(); } else { super.insertString(offset, str, a); } } } 29 20 B.4 BooleanExpr.java java.util.Vector; java.util.NoSuchElementException; java.util.Enumeration; java.io.Serializable; import import import import public class BooleanExpr implements Serializable private String value = ""; private Vector words; private Vector spaces; private Vector variables; { 10 private SpecContext context; private SpecLocation location; public BooleanExpr(SpecContext c, SpecLocation context = c; location = 1; value = words = spaces = variables 1) { 20 I; new VectorO; new VectorO; = new Vectoro; } public BooleanExpr(SpecContext c, SpecLocation 1, String s) if (s == null) { s= { "";. } 30 context = c; location = 1; value = ""; words = new Vectoro; spaces = new VectorO; variables = new Vectoro; setValue(s); // 40 does the checks for us } public void setValue(String s) { // Set the value of the expression to something generated // by the given string. Does parsing of the value, and updates // the records of variable usages. if (s.trim().equals(value.trim())) { // the string didn't change, except for surrounding // whitespace. // (Do we still want to continue in this case, to find // any words which had been defined as variables since // we last lookeded at this Expr??? - FIXME 30 50 return; } 60 words = new VectorO; spaces = new VectorO; // updates words and spaces SpecExprParsing.splitString(s, " \t\n\r", words, spaces); Vector oldVariables = variables; variables = SpecExprParsing.loadVariables(words, location, context); SpecExprParsing.unloadVariables(oldVariables, location, context); 70 // ideally, after this, instead of just keeping a flat list // of words, some true parsing of operators and such would //go on. - FIXME // Should update value/s based on the little bit of parsing // just done. i.e.: update subscripts to correspond var type, // canonicalize whitespace. - FIXME // Should eventually be able to do more sophisticated things // // 80 like link vars to definitions, mark other words with '-'s as errors, and check syntax of expression, etc. - FIXME System.out.println("BooleanExpr.setValue() location + "\n" called in " + + "context:\n" + context + "old value: \"" + value + "\"1\n" + "new value: \"" + s + "\"\n"); 90 value = s; } public String toString() { // right now toString is being used to setText in the editor, // so we'll need to fix that if we want to change things. return(value); 100 } } B.5 import import import import import BooleanExprEd.java java.awt.Component; java.util.*; javax.swing.table.*; javax.swing.*; javax.swing.event.*; public class BooleanExprEd extends JTextField implements TableCellEditor 31 { protected transient Vector listeners; protected transient BooleanExpr originalValue; 10 public BooleanExprEd() { // make a normal JTextField super(; // changing the alignment when editting is useful for debugging //setHorizontalAlignment(JTextField.CENTER); // Scroll back to the left setScrollOffset(O); 20 listeners = new VectorO; } public Component getTableCellEditorComponent(JTable Object value, table, boolean isSelected, int row, int column) if (value == null) { 30 { return(this); } if (!(value instanceof BooleanExpr)) { throw new IllegalArgumentException("BooleanExprEd can only be" + used on BooleanExpr's."); } 40 originalValue = (BooleanExpr)value; setText(((BooleanExpr)value).toString()); table.setRowSelectionInterval(row, row); table.set ColumnSelectionInterval (column, column); System. out.println("BooleanExprEd.getTableCellEditorComponent" + " called with arguements:\n" + "value: \"" + value + "\"\n" + "isSelected: \"" + isSelected + "\ "\n" + 50 "row: \"" + row + "\"A\n " + "column: \"" + column + "\"\n" + return(this); } 60 public void cancelCellEditing() { fireEditingCanceledo; } public Object getCellEditorValue( { 32 System. out.println( "BooleanAtomEd.getCellEditorValue called when:\n" + "old value was: \"" + originalValue + "\"\n" + "new value is: \"" + getText() + "\ "\n" + 70 7/ does checking and updating of the usage count for us originalValue.setValue(getText(); return(originalValue); } { public boolean isCellEditable(EventObject eo) public boolean shouldSelectCell(EventObject eo) return true; } 80 { return true; } public boolean stopCellEditing() fireEditingStopped(; return true; { 90 } public void addCellEditorListener(CellEditorListener listeners.addElement(cel); cel) { } public void removeCellEditorListener(CellEditorListener cel) { 100 listeners.removeElement(cel); } protected void fireEditingCanceled() setText(originalValue.toString(); { ChangeEvent ce = new ChangeEvent(this); for (int i = listeners.sizeo; i >= 0; i--) { ((CellEditorListener)listeners.elementAt(i)).editingCanceled(ce); } 110 } protected void fireEditingStopped( { ChangeEvent ce = new ChangeEvent(this); for (int i = listeners.size() - 1; i >= 0; i--) { ((CellEditorListener)listeners.elementAt(i)).editingStopped(ce); } } 120 } 33 B.6 import import import import SpecExprParsing.java java.util.Vector; java.util.Enumeration; java.util.NoSuchElementException; java.util.StringTokenizer; public class SpecExprParsing { // characters that are not allowed inside of names public static final char[] ILLEGAL-CHARS = { 10 Y* ,, 20 }; // // // // // / // // '-' is not here, because it's used so much, but that means that names *must* be seperated from operators by whitespace, otherwise we can't tell the variable named "alt-high" from the expression "alt" minus "high". The other operators are still excluded from being in variable names in order to avoid still more confusion. 30 // Strings that are allowed to stand by themselves // as operators. (This basically includes everything // used as a word in a boolean expression, excluding // the names of variables/states/macros.) // Not Used! public static final String[] OPERATORS = { 1>11, 40 ")", "FROM" }; 50 public static void splitString(String s, String delimiters, Vector words, Vector spaces) { // modifies words and spaces to hold the words and intervening spaces from s, // where what constitutes a space is defined by the delimiters. 34 StringTokenizer st = new StringTokenizer(s, delimiters, true); String curSpace = ""; while (st.hasMoreTokenso) { String token = st.nextTokeno; 60 if ((token.length() == 1) && (delimiters.indexOf(token) != -1)) { // } it's a delimter curSpace = curSpace.concat(token); else { // it's a word words.addElement(token); 70 // save the previous run of spaces, since it's complete spaces.addElement(curSpace); curSpace = } } } public static Vector loadVariables(Vector words, SpecLocation location, SpecContext context) Vector variables = new Vectoro; { 80 Enumeration e = words.elementsO; while (e.hasMoreElementso) { String word = (String)e.nextElement(; // this may result in a variable that appears // twice in the expression appearing twice in // the variables vector and having it's usage // location added twice. But that doesn't really // matter as long as it's deleted twice too! try { // variable names cannot contain '.'s, so if there is one, it must be there to indicate // type, and so we need to trim it before we // do the lookup. String varname; if (word.lastIndexOf('2) I= -1) { varname = word.substring( 0, word.lastIndexOf(' _)); 7/ need to check that the rest *is* just the 7/ type, like it's supposed to be. - FIXME } else { varname = word; 90 // 100 } SpecVar var = context.getVarNamed(varname); var.addUsage(location); variables.addElement(varname); } catch (NoSuchElementException ex) { 7/ just means it wasn't a defined variable } 35 110 } return(variables); } 120 public static void unloadVariables(Vector variables, SpecLocation location, SpecContext context) // unloads the given variables from the location and context { Enumeration e = variables.elements(); while (e.hasMoreElementso) { String varname = (String)e.nextElement(; // remove each old variable's usage. This will 130 // work okay, because any that are still in the // new expression will have just been added a // second time, and so this code will just remove // // // the second usage. So it's not too inefficient. (And really, since a BooleanExpr is only going to consist of 1-15ish words, this isn't bad.) SpecVar var = context.getVarNamed(varname); var.delUsage(location); 140 I } } B.7 TestStuff.java import javax.swing.table.*; import import import import javax.swing.*; java.awt.*; java.awt.event.*; java.io.*; public class TestStuff { public static OutputCommandComp myc; 10 public static void main(String[] args) try { { JPanel panel = new JPanel(); panel.setLayout(new BorderLayout(); SpecContext sp = new SpecContext(; sp.addDef("varl", "variable", 20 new SpecLocation("example location of def for varn")); sp.addDef("macro I", "macro", new SpecLocation("example location of def for macrol")); sp.addDef("state3", "state", new SpecLocation("example location of the def for state 3")); 36 SpecVar s = sp.getVarNamed(" state3"); s.addUsage(new SpecLocation( "example location of some usage of state3")); s.addUsage(new SpecLocation("example location of some other usage of state3")); InputStream is = new FilelnputStream("example.save"); ObjectInputStream ois = new ObjectlnputStream(is); 30 myc = new OutputCommandComp(sp, new SpecLocation("the command output panel")); myc = new OutputCommandComp(ois); panel.add(myc); JButton saveB = new JButton("Save"); saveB.addActionListener(new ActionListenerO { public void actionPerformed(ActionEvent ev) { 40 try { System.out.println("starting. OutputStream os = . \n"); new FileOutputStream("example. save"); ObjectOutputStream oos = new ObjectOutputStream(os); myc.saveSelf(oos); } catch (Exception ex) ex.printStackTrace(; { 50 } } panel.add(saveB, BorderLayout.SOUTH); JFrame f = new JFrameo; f.addWindowListener(new BasicWindowMonitorO); 60 JScrollPane jsp = new JScrollPane(panel); jsp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL-SCROLLBARALWAYS); jsp.setHorizontaIScrollBarPolicy(JScrollPane.HORIZONTAL-SCROLLBARALWAYS); f.getContentPane(.add(jsp); f.pack(; f.setVisible(true); 70 } catch (Exception e) e.printStackTraceo; { } } } 80 37 B.8 OutputCommandComp.java import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.Vector; public class OutputCommandComp extends JPanel static Object[] fieldnames = { { "Destination", "Acceptable Values", new Object[J { "Units", "Granularity", "Exception Handling", "Hazardous Values" 10 },I "Timing Behavior", new Object[] { "Initiation Delay", "Completion Deadline", "Output Capacity Assumptions", new Object[] { "Load", "Min. time between outputs", "Max. time between outputs", 20 }1 "Hazardous timing behavior", "Exception-Handling" }, "Feedback Information", new Object[] { "Variables", "Values", "Relationship", "Min. time (latency)", "Max. time", "Exception Handling" 30 }, "Reversed By", "Comments", "References" static Object[] tables = new Object[I { 40 { new Object[] { new Object[] {"", "", ""}, new Object[] {"", "", ""} 50 38 }; } } Vector textBoxes; Vector valueNames; Vector valueTables; 60 SpecContext context; SpecLocation location; public OutputCommandComp(SpecContext context, SpecLocation location) superO; { this.context = context; this.location = location; 70 textBoxes = CreateBoxes(fieldnames, context, location); valueNames = new VectorO; valueTables = new Vectoro; for (int i=O; i < tables.length; i++) { JTextField tf = new JTextField((String)((Object [])tables[i])[O]); valueNames.addElement(tf); Vector tableData = util.ArrayToVector((Object [])((Object [])tables[i])[11); BooleanTableComp t = new BooleanTableComp( tableData, context, location.plus("table #" + (i + 1) + " in")); valueTables.addElement(t); 80 } putThingsInPanel(textBoxes, valueNames, valueTables); } 90 private void putThingsInPanel(Vector textBoxes, Vector valueNames, Vector valueTables) { setLayout(new BoxLayout(this, BoxLayout.Y.AXIS)); AddTextBoxes(textBoxes, this); // put a 10 pixel margin around everything setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); 100 add(Box.createRigidArea(new Dimension(0,20))); JLabel def = new JLabel("DEFINITION"); Font defFont = new Font(def.getFont(.getName(, def.getFont(.getStyle(, def.getFonto.getSize() + 2); def.setFont(defFont); def.setBorder(BorderFactory.createEmptyBorder(O,200,0,0)); add(def); 39 AddTables( valueNames, valueTables ); 110 private Vector CreateBoxes(Object[] names, SpecContext context, SpecLocation location) Vector textBoxes = new Vector(; for { (nt i = 0; i < names.length; i++) { if (names[il instanceof String) { SpecTextBox field = new SpecTextBox((String)names[i], context, location.plus("in the field \"" + names[i] + "\" of")); textBoxes.addElement(field); } else 120 { if (names[i] instanceof Object[]) { textBoxes.addElement(CreateBoxes((Object[ ])names[i}, context, location)); } else { // shouldn't have any other types in here throw new IllegalArgumentExceptionO; } } 130 } return(textBoxes); } private void AddTextBoxes(Vector boxes, JPanel p) { 140 for ( int i = 0; i < boxes.sizeo; i++ ) { if (boxes.elementAt(i) instanceof SpecTextBox) { SpecTextBox field = (SpecTextBox)boxes.elementAt(i); field.setAlignmentX(Component.LEFTALIGNMENT); p.add(field); p.add(Box.createRigidArea(new Dimension(0, 5))); } else { if (boxes.elementAt(i) instanceof Vector) { JPanel innerp = new JPanel(); innerp.setLayout(new BoxLayout(innerp, BoxLayout.YAXIS)); innerp.setAlignmentX(Component.LEFTALIGNMENT); 150 // recurisively add in the other fields AddTextBoxes((Vector)boxes.elementAt(i), innerp); // indent by 60 pixels JPanel indentp = new JPanel(); 160 indentp.setLayout(new BoxLayout(indentp, BoxLayout.X-AXIS)); indentp.setAlignmentX(Component.LEFT...ALIGNMENT); indentp.add(Box.createRigidArea(new Dimension(20,0))); indentp.add(innerp); p.add(indentp); } else // { shouldn't have any other types in here 40 throw new IllegalArgumentException(; } 170 } } } private void AddTables( Vector valueNames, Vector valueTables for ){ (nt i = 0; i < tables.length; i++) { JPanel p = new JPanel(); p.setLayout(new BoxLayout(p, BoxLayout.YAXIS)); JPanel p2 = new JPanel(); p2.setLayout(new BoxLayout(p2, BoxLayout.X.AXIS)); p2.add(new JLabel(" = ")); JTextField tf = (JTextField)valueNames.elementAt(i); p2.add(tf); p2.setBorder(BorderFactory.createEmptyBorder(2,2,2,2)); p.add(p2); 180 190 BooleanTableComp t = (BooleanTableComp)valueTables.elementAt(i); t.setBorder(BorderFactory.createEmptyBorder(2,25,2,2)); p.add(t); p.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); p.setAlignmentX(Component.LEFT-ALIGNMENT); add(p); } } 200 public void saveSelf(java.io.ObjectOutputStream out) throws java.io.IOException { // This method saves this to the stream // we can't just use straight serialization, because because Swing's // implementation is going to change soon, and so is no good for saving // documents! // have to manually process the Vectors, because they're full of things 210 7/ that can't be serialized, but rather have to be saveSelf'd. saveTextBoxes(textBoxes, out); out.writeObject( new Integer(valueNames.size() ); for (nt i=0; i <valueNames.size(); i++) { String s = ((JTextField)valueNames.elementAt(i)).getText(; out.writeObject(s); } out.writeObject( new Integer(valueTables.sizeo) ); for (nt i=0; i<valueTables.sizeo; i++) { ((BooleanTableComp)valueTables.elementAt(i)).saveSelf (out); I out.writeObject(context); out.writeObject(location); 41 220 I 230 private void saveTextBoxes(Vector textBoxes, java.io. ObjectOutputStream out) throws java.io.IOException { // Because textBoxes may be a recursive Vector of Vector, we have to // process it a little bit differently. For each element, we have to // save whether it's a subvector or not, so that we know whether to recurse // or to just call the SpecTextBox constructor. (Although each sub-vector // // // starts with an Integer (the length) and not a SpecTextBox, we can't detect that on the fly, because reading the Integer is done with readObject, and reading the SpecTextBox is done with a SpecTextBox constructor. 240 out.writeObject( new Integer(textBoxes.size() for (int i=0; i<textBoxes.sizeo; i++) ); { if (textBoxes.elementAt(i) instanceof SpecTextBox) { out.writeObject(new Boolean(false)); // isn't a Vector ((SpecTextBox)textBoxes.elementAt(i)).saveSelf (out); } else { if (textBoxes.elementAt(i) instanceof Vector) { out.writeObject(new Boolean(true)); // *is* a Vector saveTextBoxes ((Vector)textBoxes.elementAt (i), out); } else { // should never get here throw new IllegalArgumentException(; } } 250 } } private Vector loadTextBoxes(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { 260 // See the comment for saveTextBoxes() on why this has to be done this way Vector textBoxes = new VectorO; int for tbSize = ((Integer)in.readObject().intValue(; (nt i=0; i<tbSize; i++) { Boolean isSubVector = (Boolean)in.readObjecto; if (isSubVector.booleanValueo) { textBoxes.addElement( loadTextBoxes(in) ); } else { textBoxes.addElement( new SpecTextBox(in) ); 270 } } return(textBoxes); } public OutputCommandComp(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { // This constructor is equivalent to a "load" method. // we can't just use straight serialization,because because Swing's // implementation is going to change soon, and so is no good for saving // documents! 42 280 super(; // load stuff from stream textBoxes = loadTextBoxes(in); 290 valueNames = new Vectoro; int vnSize = ((Integer)in.readObjecto).intValue(; for (int i=O; i<vnSize; i++) { valueNames.addElement( new JTextField( (String)in.readObject() } ) ); valueTables = new Vectoro; int vtSize = ((Integer)in.readObject().intValue(; for (nt i=0; i<vtSize; i++) { valueTables.addElement( new BooleanTableComp(in) ); 300 } context location = = (SpecContext)in.readObject(; (SpecLocation)in.readObject(; // reconstruct the rest putThingsInPanel(textBoxes, valueNames, valueTables); } 310 B.9 SpecTextBox.java import import import import javax.swing.*; java.awt.*; java.awt.event.*; java.util.Vector; public class SpecTextBox extends JPanel implements FocusListener private JLabel 1; private JTextArea ta; { 10 private SpecContext context; private SpecLocation location; private private private private private String String Vector Vector Vector name; value; words; spaces; variables; public SpecTextBox(String name, SpecContext context, SpecLocation location) super(; this.name = name; constructRest(name); 43 { 20 value = ta.setText(value); 30 words = new Vectoro; spaces = new Vector(; variables = new VectorO; this.context = context; this.location = location; } private void constructRest(String name) { // sets value, ta, and 1, and adds everything to the panel 40 setLayout(new BoxLayout(this, BoxLayout.XAXIS)); 1 = new JLabel(name + ":"); L.setAlignmentY(Component.TOPALIGNMENT); // can set to height to one, as it will expand as needed. ta = new JTextArea(1, 30); ta.setBorder(BorderFactory.createEtchedBorder(); 50 ta.setLineWrap(true); ta.setWrapStyleWord(true); ta.setAlignmentY(Component.TOP-ALIGNMENT); // // set the FocusListener, so that we can process its contents when it loses focus. ta.addFocusListener(this); add(l); 60 add(Box.createRigidArea(new Dimension(5,0))); add(ta); } public JTextArea getJTextArea() return(ta); { } 70 public void focusGained(FocusEvent e) // don't care about gaining focus { } void setValue(String s) { // update the words and variables Vectors, and correspondingly // update the context based on what in the usage has changed. if (s.trim().equals(value.trim()) { // // the string didn't change, except for surrounding whitespace. // // (Do we still want to continue in this case, to find any words which had been defined as variables since // we last lookeded at this Expr??? - FIXME 44 80 return; } words = new Vectoro; spaces = new VectorO; // updates words and spaces SpecExprParsing.splitString(s, " \t\n\r, 90 .",0 words, spaces); Vector oldVariables = variables; variables = SpecExprParsing.loadVariables(words, location, context); SpecExprParsing.unloadVariables(oldVariables, location, context); // // Should update value/s based on the parsingjust done. 7/ Should eventually be able to do more sophisticated things to definitions, mark other words with '...'s 100 i.e.: update subscripts to correspond var type. - FIXME // like link vars // as errors, and check syntax of expression, etc. - FIXME System.out.println("SpecTextBox. setValue ) called in " + location + "\n" + "context:\n" + context + "old value: \" "new value: \"" 110 + value + "1\"1\n" + + s + "\"\n"); value = s; I public void focusLost(FocusEvent e) { // parse the contents of the TextArea which just lost 7/ focus. 7/ 120 update various values, based on the current text in the TextArea setValue(getJTextAreao.getText()); I public void saveSelf(java.io.ObjectOutputStream out) throws java.io.IOException { // This method saves this to the stream /7 /7 7/ we can't just use straight serialization, because because Swing's 130 implementation is going to change soon, and so is no good for saving documents! out.writeObject(name); out.writeObject(value); // // 7/ even those these are derived from value, save them too, so that we don't have to worry about whether variables are loaded from the file before or after their definitions. out.writeObject(words); out.writeObject(spaces); out.writeObject(variables); out.writeObject(context); 45 140 out.writeObject(location); I public SpecTextBox(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { // This constructor is equivalent to a "load" method. // we can't just use straight serialization, because because Swing's // implementation is going to change soon, and so is no good for saving // documents! 150 super(; // load stuff from stream name = (String)in.readObject(; 160 // create derived things, and add them to the panel constructRest(name); // these affect some things set in constructRest() value = (String)in.readObject(; ta.setText(value); words = (Vector)in.readObject(; spaces = (Vector)in.readObject(; variables = (Vector)in.readObjecto; 170 context = (SpecContext)in.readObjecto; location = (SpecLocation)in.readObjecto; } } B.10 import import import import import BooleanTableComp.java javax.swing.*; javax.swing.table.*; java.awt.*; java.awt.event.*; java.util.Vector; public class BooleanTableComp extends JPanel { private JTable t; 10 private BooleanTableModel btm; private SpecContext context; private SpecLocation location; public BooleanTableComp(SpecContext c, SpecLocation 1) { this(null, c, 1); } public BooleanTableComp(Vector TableData, SpecContext c, SpecLocation 1) superO; 46 { 20 context = c; location = 1; if (TableData == null) { btm = new BooleanTableModel(context, location); } else { btm = new BooleanTableModel(TableData, c, 1); } 30 t = constructTable(context, location, btm); putThingsInPanel(t); } private JTable constructTable(SpecContext c, SpecLocation 1, BooleanTableModel btm) { JTable t; BooleanTableColumnModel tcm = new BooleanTableColumnModel(); t = new JTable( btm, tcm ); t.setAutoCreateColumnsFromModel(true); t.setBorder(BorderFactory.createEtchedBorder(); 40 t.setDefaultEditor(BooleanAtom.class, new BooleanAtomEdo); t.setDefaultEditor(BooleanExpr.class, new BooleanExprEd(); return(t); } 50 private void putThingsInPanel(JTable t) { // puts the table and some other things in the panel. // used by both deserializationand the constructor. JButton rowB = new JButton("New Row"); rowB.setMnemonic( 'R'); rowB.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ev) { btm.addEmptyRowO; 60 } }); JButton colB = new JButton("New Column"); colB.setMnemonic( 'C'); colB.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ev) btm.addEmptyColumn(; { } 70 }); // 7/ //- The mnemonics don't actually work, because everything I try just tries to edit the table part of things. FIXME, somehow JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.Y.AXIS)); buttonPanel.add(rowB); buttonPanel.add(colB); 80 47 // The table should be in it's own scrollpane, to keep // // // the buttons in sight at all times. But when we put it in one, the headers become visible, and the default size becomes weird. - FIXME setLayout(new BoxLayout(this, BoxLayout.X-AXIS)); add(t); add(Box.createRigidArea(new Dimension(5,0))); add(buttonPanel); 90 } public void saveSelf(java.io.ObjectOutputStream out) throws java.io.IOException { // This method saves this to the stream // we can't just use straight serialization, because because Swing's // implementation is going to change soon, and so is no good for saving // 100 documents! btm.saveSelf (out); out.writeObject(context); out.writeObject(location); } public BooleanTableComp(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { // This constructor is equivalent to a "load" method. // we can't just use straight serialization, because because Swing's // implementation is going to change soon, and so is no good for saving // documents! // load stuff from stream btm = new BooleanTableModel(in); context = (SpecContext)in.readObjecto; location = (SpecLocation)in.readObject(; } B.11 110 120 // reconstruct the rest t = constructTable(context, location, btm); putThingsInPanel(t); // initialize our parent BooleanTableModel.java import javax.swing.table.*; import javax.swing.*; import java.util.Vector; public class BooleanTableModel extends AbstractTableModel 48 { private private private private Vector exprs = new VectorO; Vector atoms = new VectorO; SpecContext context; SpecLocation location; 10 public BooleanTableModel(SpecContext c, SpecLocation 1) { supero; context = c; location = 1; // make sure that all tables have at least two columns and one row Vector v = new VectorO; v.addElement(new BooleanExpr(context, location.plus("row #1 of"))); v.addElement(new BooleanAtomo); 20 addRow(v); } public BooleanTableModel(Vector v, SpecContext c, SpecLocation 1) { // requires: vector v must be a vector of vectors full of data to // put into the table, and there must be at least one subvector (row) // which must contain at least 2 elements (columns), and the first 30 super(; context = c; location for = 1; (nt r = 0; r < v.sizeo; r++) { Vector row = (Vector)v.elementAt(r); 40 addRow(row); } } public void addColumn(Vector columnData) { // add a new column of BooleanAtoms to the table if (columnData.sizeo != getRowCount()) { throw new IllegalArgumentException("The number of elements to be" + " added must match the size of" + " the column in the table."); 50 } Vector v = new VectorO; for (int r=0; r < getRowCount(; r++) { if (columnData.elementAt(r) instanceof String) { 60 // call BooleanAtom constructor on Strings v.addElement( new BooleanAtom((String)columnData.elementAt(r)) } else { // will cause an exception if the type is wrong v.addElement((BooleanAtom)columnData.elementAt(r)); } } 49 ); atoms.addElement(v); // tell listeners that a new column has been added 70 fireTableStructureChanged(; } public void addRow(Vector rowData) { // add a new row to the table (must consist of // one BooleanExpr and the rest BooleanAtoms // if there are no columns yet, you don't have to match the // current number of columns! 80 if ((getRowCount() != 0) && (rowData.sizeo != getColumnCount()) { throw new IllegalArgumentException("The number of elements to be added" + " must match the size of the row in" + " the table."); } if (rowData.elementAt(0) instanceof String) { exprs.addElement( new BooleanExpr( context, location.plus("row #" + (getRowCounto + 1) + (String) rowData.elementAt(0)) ); } else { // this is actually relatively weird, because how can they know the // location before it's put in the table??? but with the string case // taken care of above, and the add empty below, it doesn't matter // too much! - FIXME, sarac " of"), 90 // will cause an exception if the type is wrong exprs.addElement((BooleanExpr)rowData.elementAt(0)); } 100 // getRowCount() will always say there is at least one row, since we just added the expression, so we have to subtract 1. if (getRowCount( == 1) { // special case out the first row // for (nt c=l; c < rowData.sizeo; c++) { Vector v = new VectorO; BooleanAtom ba; if (rowData.elementAt(c) instanceof String) { ba = new BooleanAtom((String)rowData.elementAt(c)); } else 110 { // will cause an exception if the type is wrong ba = (BooleanAtom)rowData.elementAt(c); } v.addElement(ba); atoms.addElement(v); } 120 } else { // not the first row for (nt c=1; c < rowData.sizeo; c++) { // will cause an exception if the type is wrong BooleanAtom ba; if (rowData.elementAt(c) instanceof String) 50 { ba = new BooleanAtom((String)rowData.elementAt(c)); } else { ba = (BooleanAtom)rowData.elementAt(c); } ((Vector)atoms.elementAt(c-1)).addElement(ba); 130 } } // tell listeners that a row was added to the end fireTableRowsInserted(getRowCount() - 1, getRowCount() - 1); } public void addEmptyColumn() { // add a new column to the table made up of // some BooleanAtom's created with the default constructors 140 Vector v = new VectorO; for (int r=0; r < getRowCount(; r++) v.addElement(new BooleanAtomo); { } addColumn(v); 150 } public void addEmptyRow () { // add a new row to the table made up of a BooleanExpr // and some BooleanAtom's created with the default constructors Vector v = new VectorO; v.addElement(new BooleanExpr(context, location.plus(" row #" + (getRowCount( + 1) + of "))); for (int c=1; c < getColumnCount(; c++) v.addElement(new BooleanAtomo); 160 { } addRow(v); } 170 public int getRowCount() return exprs.sizeo; { } public int getColumnCount() if (atoms.sizeo == 0) { return(1); } else { { 180 return( atoms.size() + 1); } } 51 public Class getColumnClass(int c) if (c == 0) { return(BooleanExpr.class); } else { return(BooleanAtom.class); { 190 } } public String getColumnName(int c) return("col #" + (c + 1)); { } public boolean isCellEditable(int r, int c) public Object getValueAt(int r, int c) if (c == 0) } { return(true); } 200 { { return(exprs.elementAt(r)); else { return(((Vector)atoms.elementAt(c - 1)).elementAt(r)); } } 210 public void setValueAt(Object value, int r, int c) { if (c == 0) { if (!(value instanceof BooleanExpr)) { throw new IllegalArgumentException("Only BooleanExpr values can go in" + " the first column of the table."); } } exprs.setElementAt(value, r); else { if (!(value instanceof BooleanAtom)) { throw new IllegalArgumentException("Only SimpleBoolean values can go" + in the later columns of the table."); 220 } ((Vector)atoms.elementAt(c - 1)).setElementAt(value, r); } fireTableCellUpdated(r, c); } 230 public void saveSelf(java.io.ObjectOutputStream out) throws java.io.IOException { // This method saves this to the stream // // // we can't just use straight serialization, because because Swing's implementation is going to change soon, and so is no good for saving documents! out.writeObject(exprs); out.writeObject(atoms); out.writeObject(context); out.writeObject(location); 240 } 52 public BooleanTableModel(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { // This constructor is equivalent to a "load" method. // we can't just use straight serialization, because because Swing's // implementation is going to change soon, and so is no good for saving // 250 documents! supero; exprs = (Vector)in.readObject(; atoms = (Vector)in.readObjecto; context = (SpecContext)in.readObjecto; location = (SpecLocation)in.readObject(; } 260 // debugging method, for dumping the data in the table. public void dumpo { System.out.println(" exprs: 11+ exprs); System.out.println("atoms: "+ atoms); for (nt r = 0; r < exprs.sizeo; r++) { System.out.print(" i "); System.out.print(exprs.elementAt(r) + "i"); 270 for } c = 0; c < atoms.sizeo; c++) { System.out.print(((Vector)atoms.elementAt(c)).element At(r) + "I"); (nt System.out.println(; } } 280 B.12 BooleanTableColumnModel.java import javax.swing.table.*; public class BooleanTableColumnModel extends DefaultTableColumnModel public BooleanTableColumnModel() super(; { { public void addColumn(TableColumn aColumn) { if (aColumn.getModelIndexo != 0) { // should find out width of one char in current font, // instead of hard-coding this. - FIXME, sarac aColumn.setPreferredWidth(15); aColumn.setMinWidth(15); aColumn.setMaxWidth(15); } 53 10 super.addColumn(aColumn); 20 } } B.13 SpecLocation.java import java.io.Serializable; { public class SpecLocation implements Serializable private String value; // I have *no* idea how these will actually be eventually implemented. // // URLS? Document Positions? Copies of objects? // need a real, non-trivial implementation, that has support for jumping to locations, etc. That will cause almost // everything to change. - FIXME public SpecLocation(String s) value = s; 10 { } public SpecLocation plus(String s) { // a method to take part of a SpecLocation, and // combine it with this SpecLocation to produce a // a new one. // 20 // // with the current implementation, it takes a String and we just concat that string onto the front of this.toString, but there are much better ways. 7/ // (if we were doing URLs or directories or chapter/section/ figure numbers, we might concat onto the end.) 30 return( new SpecLocation(s + " + this.toString() ); } public boolean equals(SpecLocation otherSL) { // seems like a reasonable definition, especially // given our current [lack of] implementation for // SpecLocation. return( toString(.equals(otherSL.toString() ); } 40 public boolean equals(Object otherSL) { if (otherSL instanceof SpecLocation) { return( equals( (SpecLocation)otherSL } else { // if it's not a SpecLocation, it's 7/ not the same. ) ); definitely return( false ); 54 } 50 } public String toString() { return(value); } B.14 import import import import SpecContext.java java.util.Hashtable; java.util.Enumeration; java.util.NoSuchElementException; java.io.Serializable; public class SpecContext implements Serializable // need to keep track of: // defined variables 77 defined macros { 10 // each has: // a name (alpha-numeric, no [white]spaces, no underscores?) 7/ /7 // a type (variable, macro, state) the location of its definition a list of places where it appears // don't allow underscores in names, that way we can use them 7/ as a notation for subscripts // don't allow whitespace in names, so that it's clear where they 7/ end and begin when appearing in text. // // probably going to want the list itself to be a hashtable, which means "names" will have to be hashable. // Keeping things SYNCronized: 7/ // 7/ everytime something that uses a definition is updated, any new usages that are added need to be added to the table under the variable used. 20 30 // any old usages that are removed need to be removed from the // table under the variable no longer used. 7/ // 7/ 7/ everytime something that creates a definiton is updated, if a new definition is created, it's added to the table if an old definition is removed, it can only be removed if the variable doesn't appear anywhere (better than leaving dangling links - design decision). // 7/ 7/ // how to represent locations? if we keep a pointer to the object, we may be able to find out where it is, but what if the object is removed from the 55 40 // document without // // // // // 7/ // telling us? when something is removed, every variable it uses needs to be updated in the table. - (prevents nastier schemes, and no more onerous than making defining objects remove all usages before letting them be removed.) - this way moving the objects won't be a problem. - for this to work, must always use the "same" objects, rather // 50 than replacing them arbitrarily. (ie, replacement will be 7/ // // equivalent to removal and recreation, in two steps.) could keep some reference to some sort of canonical "document that forces the URL/anchor issue now, which seems // location", but /7 7/ 77 like a good thing to avoid if we can (although we'll have to face it eventually). 77 7/ 7/ /7 7/ Can't just keep copies of objects, because we want the overall 7/ Contexts are a way of tying together a single definition with 7/ multiple usages, based on the name given to what is defined. /7 (Link between the two needs to be bi-directional, so we can / 60 context to be able to include references to other documents, I think, plus we'd have to rebuild the context every time we load a file, if they aren't more canonical than just a copy of the object where the definition/usage appears. 70 find both definitions and usages at will.) private Hashtable table; public SpecContext() { table = new Hashtableo; 80 } public void addDef(String name, String type, SpecLocation definition) throws AlreadyDefinedException, IllegalArgumentException { try { SpecVar vax = getVarNamed(name); /7 if that didn't throw an exception, the variable 90 7/ is already defined. throw new AlreadyDefinedException(" } \"" + name + "\"" "was already defined."); catch (NoSuchElementException e) { /7 do nothing, we were just checking. + } SpecVar var = new SpecVar(name, type, definition); table.put(name, var); } 100 public SpecVar getVarNamed(String name) throws NoSuchElementException 56 { SpecVar var = (SpecVar)table.get(name); if (var != null) { // found it, done return(var); 110 I // no where else to search throw new NoSuchElementException("No such key named \"" + name + "1\"1 in the given context."); } // Don't need to write add usage / get usage methods, since // the user can manipulate the SpecVar once it's been fetched. 120 public void removeVarNamed(String name) throws NoSuchElementException, StillInUseException { SpecVar var = getVarNamed(name); if (var.countUsages( != 0) { throw new StillInUseException("Couldn't remove variable \"" in use."); + "\", as it was still + name 130 I table.remove(name); } public Enumeration getVarso return(table.keys(); I { 140 public int sizeO { return(table.sizeo); } public String toString( { String answer = "\n(---- Begin Context ---- \n"; Enumeration e = getVars(; while (e.hasMoreElementso) { answer = answer + getVarNamed((String)e.nextElement()) + "\n"; 150 } answer = answer + "---- End Context ---- )\n\n"; return( answer } }I 160 57 B.15 import import import import SpecVar.java java.util.Vector; java.util.NoSuchElementException; java.util.Enumeration; java.io.Serializable; public class SpecVar implements Serializable private private private private { String name; String type; SpecLocation defLoc; Vector usageLocs; // Vector of SpecLocation's 10 public SpecVar(String name, String type, SpecLocation defLoc) throws IllegalArgumentException { for (nt i = 0; i < name.length(; i++) { // check each character for whitespace if (Character.isWhitespace(name.charAt(i))) { throw new IllegalArgumentException("No white space allowed in" + variable names."); 20 } // check each character against the illegal characters for (int j = 0; j < SpecExprParsing.ILLEGAL-CHARS.length; j++) if (name.charAt(i) == SpecExprParsing.ILLEGAL-CHARS[j]) { { throw new IllegalArgumentException("While trying to create variable named \"" + name + "\": Illegal character '" + name.charAt(i) + "' not allowed in name."); } } 30 } this.name = name; if (!(type.equals("macro") jj type.equals("state") j type.equals("variable"))) { throw new IllegalArgumentException("While trying to create \"" 40 + name + A SpecVar's type must be" + macro, state, or variable."); "\": } this.type = type; this.defLoc = defLoc; 50 this.usageLocs = new Vectoro; } public String getName() return(name); { } 58 public String getTypeo return(type); { } 60 public SpecLocation getDefLoc() return(defLoc); { } public void addUsage(SpecLocation usage) { // usages may appear more than once, and so may have // to be deleted more than once, but that's the caller's // concern. 70 // (i.e., if a variable is used more than once in a given // section, the caller can count that as more than one usage, // as long as they're consistent.) usageLocs. addElement (usage); } public void delUsage(SpecLocation usage) throws NoSuchElementException // usages may appear more than once, and so may have // to be deleted more than once, but that's the caller's // concern. { 80 // // search from the back of the vector, for a little better efficiency, given the way BooleanExpr is implemented. int position = usageLocs.lastlndexOf(usage); if (position == -1) { // usage not found throw new NoSuchElementException(" \"" + name + "\": doesn't have the usage \"" + usage + 90 "\"."); } usageLocs.removeElementAt(position); } public Enumeration getUsages() { return(usageLocs.elements()); } 100 public int countUsages() { return(usageLocs.sizeo); } public String toString() { String answer = "l; 110 answer = answer + "The " + getType() + " named \"" + getName() + "\" getNameo + "... + getTypeo.charAt(0) + ") is defined at:\n"; answer = answer + "\t" + getDefLoc() + "\n"; 59 (i.e., " + if (countUsages() == 0) { answer = answer + "and is *not* used.\n"; } else { answer = answer + "and is used in:\n"; 120 Enumeration e = getUsages(; while (e.hasMoreElementso) { answer = answer + "\t" + (SpecLocation)e.nextElement() + "\n"; } } answer = answer + "\n"; return(answer); 130 } I B.16 AlreadyDefinedException.java public class AlreadyDefinedException extends Exception public AlreadyDefinedException() superO; { { } public AlreadyDefinedException(String s) super(s); { 10 B.17 StillInUseException.java public class StillInUseException extends Exception public StillInUseExceptiono super(; { { } public StillInUseException(String s) super(s); { 10 B.18 util.java import java.util.Vector; public class util { 60 ,1 public static Vector ArrayToVector( Object[] a Vector answer = new VectorO; for (nt i = 0; i < a.length; i++) { if (a[i] instanceof Object[]) { answer.addElement(ArrayToVector( (Object[])a[i] )); else { answer.addElement(a[i]); } } ){ 10 } return( answer ); } } B.19 BasicWindowMonitor.java import java.awt.event.*; import java.awt.Window; public class BasicWindowMonitor extends WindowAdapter public void windowClosing(WindowEvent e) Window w = e.getWindowo; w.setVisible(false); w.disposeo; System.exit(O); } { { 10 } 61 Bibliography [1] Patrick Anderson. A modular framework for reusable research software. Master's thesis, Massachusetts Institute of Technology, 2000. [2] Per Cederqvist. Version management with CVS. http://www.gnu.org/manual/cvs-1.9/cvs .html, 1993. [3] Nancy Leveson. Safeware: System Safety and Computers. Addison-Wesley, 1995. [4] Nancy Leveson. Draft intent specifications (including SpecTRM-RL) user manual. Not yet published, 2000. [5] Nancy Leveson. Intent specifications: An approach to building human-centered specifications. IEEE Transactions on Software Engineering, January 2000. [6] Nancy G. Leveson. Completeness in formal specification language design for process control systems. To be published in Proceedings of Formal Methods in Software Practice, August 2000. [7] Nancy G. Leveson, Mats P. E. Heimdahl, and Jon Damon Reese. Designing specification languages for process control systems: Lessons learned and steps in the future. In Proceedings of the 7th European Engineering Conference held jointly with the 7th ACM SIGSOFT symposium on Foundations of software engineering, September 1999. [8] Philip Milne and Kathy Walrath. Long-term persistence for JavaBeans. http://java. sun. com/products/jfc/tsc/articles/persistence/index.html, 2000. [9] Sun Microsystems, Inc. Swing 1.1.1 API specification. http: //java. sun. com/products/jf c/swingdoc-api-1.1.1/, 1993-1999. 62 [10] Sun Microsystems, Inc. Java platform 1.1 API specification. http: //java. sun. com/products/jdk/1.1/docs/api/packages .html, 1995-1999. [11] Sun Microsystems, Inc. Java foundation classes: Now and the future. http: //java. sun. com/products/jf c/whitepaper .html, 2000. [12] World Wide Web Consortium (W3C). http://www.w3.org/XML/, 1994-2000. 63 Extensible Markup Language (XML).