Troubleshooting / Frequently Asked Questions
General
What are the overhead
costs of using
the text2gui library?
There is some overhead memory and execution time cost of
initializing the text2gui library, then using it to construct a
component hierarchy by parsing a resource bundle. However, it is not as
pronounced as other products that convert text into component
hierarchies for the following reasons:
- text2gui descriptions are more compact than other descriptions,
such as XML
- No XML parser is required
- The use of reflection is largely avoided
Of course, if a BeanShell script are embedded in a description, there
is an additional cost of initializing BeanShell, and using it to
interpret the script. As the number of these scripts is usually not
large, the cost of using BeanShell is modest.
Once a component hierarchy is created, the components behave just as
fast as they would if constructed manually. This is because instances
of the actual Swing components are created, not proxies for Swing
components. The cost of communicating with a component hierarchy
through an argument map after
construction is not noticeable to the user, because so few map keys are
updated per time period.
Listeners implemented by BeanShell scripts embedded in a resource
bundle description do run slower than comparable listeners implemented
with compiled Java code. Again, this is typically not noticable since
listeners are notified infrequently and they don't do much besides
making a few calls to other objects. (If a listener calls a method
implemented with compiled Java code, the method runs just as fast as if
it were called by compiled code.)
I have not found the
startup and response times to be significantly more than if components
were created with Java code, and all listeners were implemented with
Java code.
Why doesn't text2gui use XML to
describe component hierarchies, like other products?
XML has the advantage that its general syntax is well-known, and
that specializations of the syntax can be specified with a simple text
file, the Document Type Declaration (DTD). However, XML
has the following disadvantages compared to the text2gui syntax:
- An external XML parser is required. The footprint of an XML
parser alone, without the ability to create components, may be bigger
than the entire text2gui library.
- Typically, XML parsing requires more processing time because of
its generality
- Due to its hierarchical structure, inheritence / overridding of
property values is clumsy and difficult. In particular, this makes i18n
applications difficult to implement.
- The XML syntax is very sensitive to textual errors. If an XML
document is not well-formed, the entire contents may be discarded. On
the other hand, textual errors in text2gui code typically affect a
single property.
- Due to the requirement of end tags, the XML syntax is more
verbose than the text2gui syntax
The text2gui syntax was designed specifically for describing component
hierarchies, so it is no surprise that it is more suitable for this
purpose
than XML.
How big are the library files I need to ship with my application so
it can use the text2gui library?
The text2gui library is about 600 KB. In order for BeanShell to
work, you also need to ship the BeanShell library, which is about 300
KB. (It is possible to reduce the size by getting a subset of the
BeanShell functionality, see the BeanShell homepage for details.) If
you already use log4j, there's no need to account for the size of the
log4j
library, but if you don't, you can just ship the facade version of the
log4j library which is only 1 KB. So the total extra size, under
ordinary circumstances, would be about 900 KB.
Can I use the text2gui library for
applets?
Yes! But be aware that BeanShell won't work in most
applet environments, due to its
reliance on reflection which is a security risk. You can circumvent
this problem by signing your applet, then having the user grant all
privileges to it.
Since BeanShell scripts won't work, you'll have to perform all code
using ordinary Java code. This includes implementing all listeners and
all from and to map converters. It would probably be wise just put the
value needed by the component converters directly in the map, without
relying on any conversion routines. This may breakdown some of the
separation between the model and view/controller, but it's a good
compromise.
Remember you can always just use the text2gui library to perform
component construction, then pick out the components you want to
control from the global variable space. You aren't forced to use the
MVC architecture if it's too painful.
What IDE's can I use with text2gui?
Unlike other tools which aid Swing development, the text2gui
library and development kit is IDE independent. You can use any IDE to
edit the Java code that calls the text2gui library or text2gui
descriptions of component hierarchies. After all, these are just text
files!
For testing components described with text2gui code, the text2gui
development kit supplies the GUI Editor
application which makes testing a breeze! But you are in no way forced
to use it.
After describing your component hierarchies with text2gui code, and
testing them immediately with the GUI Editor, you'll find other GUI
construction tools very clumsy. No mouse dragging, resizing, searching
for the right property to change, etc. is required. Contrary to the
adage, "A picture is worth a thousand words", editing text is
still the easiest way of programming.
How do
I use text2gui to create my own
subclass of a component class?
You might want to create your own subclass of Component so that others can easily
use your class in their component hierarchies. But using text2gui to
manually create each of the individual child component and property
values would be a pain. The solution is to use text2gui to configure your subclass of Component. Configuration can set
the contents and some of the property values of your component, but it
obviously can't handle the new property values specific to your
subclass. The management of these properties must be done with your
subclass.
See Composite
Configuration with a
Dispatching Converter for details on how to configure an instance
of a composite object -- in this case you would be configuring the this pointer.
If you have the development kit already, the source code for
com.taco.swinger.FontChooser provides
an example of doing this.
Can I use the
text2gui infrastructure
to create instances of a custom type?
Yes! There are a variety of reasons to do this. You may simply want to
convert keys in resource bundles to objects directly used by your
application. Or you may want to install your own kind of
component/border/layout converter so that your component hierarchy
descriptions can use them.
Sometimes the conversion process doesn't require any parameters, for
example, when you need to create an object used as a single property
value. If the object created by conversion is identical each time the
conversion process runs, you can simply use the instance converter,
and specify an object created by a BeanShell script.
If you want the conversion process to be able to accept parameters, you
can still use the text2gui library to help you, but you'll have to
write some of your own Java code. text2gui can handle the details of
resolving references,
assigning to globals, etc. for you. If you want to create an atomic
converter (a converter that
creates objects with no properties), create a class, say StringToX, that implements com.taco.text.IStringToObjectConverter.
All you need to do is specify how a literal string (not a reference) is
converted to an object. Then pass an instance of StringToX to the constructor of com.taco.text.AtomicConverter. You now have an
interpolating converter to instances of class X.
To create a composite converter, the easiest way is to implement
com.taco.text.BracedPropertyCompositeConverter.
It converts resource bundle keys and strings with the braced property syntax. Then
override the following methods:
- Object
_createComposite(Map propertyMap,
ResourceBundle bundle, INoReturnMap argMap): create a new
instance of the object, using the creation properties in
property map.
- Collection
_getCreationPropertyNames(): return a collection of names of the
creation
properties of the composite. The order of the names is the order in
which the values will be created.
- Collection
_getPropertyNames(): return a collection of names of the
ordinary properties of the composite. The order of the names is the
order in which the values will be created.
- IInterpolatingConverter
_getConverterForProperty(String propertyName, Object composite):
return the converter used to convert strings and resource bundle keys
to values for the (possibly creation) property with the argument name.
- CompositeConverter.ISetPropertyAction
_getActionForProperty(String propertyName): Return a strategy
for setting the property with the argument name. If you can afford the
runtime cost of reflection, you can return CompositeConverter.ReflectionSetPropertyAction.instance.
If no action is to be taken, return null.
- (Advanced) void
_addMapConsistencyListener(Object composite, String propertyName,
IObjectMapper toMapValueConverter, INoReturnMap argMap, Object mapKey,
Method addListenerMethod): Add a listener to the composite
that ensures map consistency on the argument map key mapKey, for the property named propertyName. If your composite
object notifies property change listeners when its propertyName property is
changed, you can just use the superclass's implementation. Otherwise,
you will need to add a listener to your composite that updates the
argument map when the propertyName
property is changed.
If your listener extends CompositeConverter.AbstractMapConsistencyListener,
you can just use the _put(Object
value) method to put the new property value into the argument
map. It takes care of delayed values and to map conversion using toMapConverter.
If no map consistency is possible or required, just return null
.
It's also possible to leverage the composite converter class for a
subclass of the class you are trying to create. Let's say a converter
for class X exists in the
text2gui library. Then it would have the name XConverter. Now let's say you want
to create a converter for class Y,
which extends X. Then you
should create a converter class YConverter
that extends the class XConverter.
You would still need to implement the methods above, but the
implementations would only need to handle the properties that Y has but X does not. For the methods 4-6,
you can use the superclass's method by calling super.XXX() to handle the X's properties.
If you want your component/layout/border converter to be used by the
default component/layout/border dispatching converters, you need to
install them in the DEFAULT_INSTANCE
field of the corresponding class. See Installing Types in a Dispatching
Converter for details.
If you want to use a custom string syntax, you can extend com.taco.text.CompositeConverter
instead. You'll need to implement the methods 1-6 above, and also
implement the method
Object _literalToObject(String s, ResourceBundle bundle,
INoReturnMap argMap, String globalName)
The nextValue() methods
in com.taco.text.StringConverterUtilities
may help you in your implementation.
Setup & Distribution Issues
Must I use log4j? Must I ship
it with my application that uses text2gui?
The text2gui library does make calls to the log4j library, but only the
most basic calls. All of the calls can be handled by the tiny facade
library lib/facade/log4jfacade.jar.
The facade library simply redirects logging requests to the java.util.logging package so you
will still get log output. You may ship log4jfacade.jar with your
application. Of course, you can always use and ship the full version
too.
To run the GUI Editor, the full log4j library is required to be in the
class path.
Conversion Issues
When I try to convert a resource
bundle key, an error occurs. Why?
Remember that if a value is mapped directly to the resource bundle key,
that value will be used as the result of conversion, regardless of the
subkeys that are set. Check that a parent resource bundle doesn't set
the key directly.
At least one subkey of a resource bundle key must be set if no value is
mapped directly to the resource bundle key. If no properties need be
set right away, you may need to set a "dummy" property like name, which has no effect:
textfield.name=Dummy
#text is set later by Java code
If the object you're trying to convert is a component, layout, or
border, be sure to set the dispatchType
subkey so the dispatching converter knows what specific type of
component, layout, or border converter to use.
I can't seem to be able to set a
property of a composite object using a subkey of a base resource bundle
key.
Are you sure the base key isn't defined, possibly in a parent bundle?
If so, the subkeys will be ignored. See Composite Converters.
How are types handled by the text2gui
library?
Notice that the result of argument map interpolation, global variable
interpolation, and BeanShell scripts may be any instance of Object.
This means, for example, the string converter might return an instance
of Integer! This normally
indicates a bug, but sometimes this is a good thing. Even though the
Image Icon converter normally returns an instance of ImageIcon, by using it to convert
an argument map key, it can return an instance of Icon instead, which is all that is
needed for the icon
property of a label. Many of the properties of components are
documented with the note: "May also be set with an instance of X". This means if the property
value comes from an argument map reference or a BeanShell script, the
value can also be an instance of X
in addition to the type ordinarily created by the property value
converter.
text2gui does very little type checking. If you attempt to set a
property with the wrong type, the exception usually occurs at the setXXX() method of the
component. These kinds of exceptions will be caught, a warning will be
logged, and conversion will continue.
Argument Map Issues
Map consistency doesn't seem to be
working.
Be sure that the property that you want to be map consistent can
actually be made map consistent -- only properties of composite objects
can be map consistent, and not all composite properties can be made map
consistent either. Check the converter table for the object type you
are trying to convert.
Remember that for a property to be map consistent, a w must appear after a colon (':') after the argument map key
name, e.g. name:w.
Isn't there an infinite recursion if a
map key is both updatable and consistent?
Excellent question! You'd think there would be in the following
situation: a property p
of of a composite object is updated when the value associated with
argument map key key is
changed, and key is kept
consistent with p. Then
the following sequence
of events unfolds:
- The programmer updates the value mapped to key in the argument map.
- The updater listener installed in the argument map updates the
property value for property p
of the composite.
- The map consistency listener installed in the composite updates
the value mapped to key
in the argument map.
Notice steps 1 & 3 are the same! We have an infinite recursion, at
least that's what you'd think. But this doesn't really occur. Map
consistency listeners are careful only to notify listeners that are not
updater listeners. Thus no infinite recursion occurs. If you implement
your own map consistency listeners, you must be sure to do the same
thing. The easiest way to do this is to have your map consistency
listener extend CompositeConverter.AbstractMapConsistencyListener.
Then use the _put(Object value)
method to put the new value in the argument map. _put() is careful only to
notify the non-updater listeners of the argument map.
So in summary, it's perfectly acceptable for an argument map key to be
both updatable and consistent.
Can a property be updated based on
more than one argument map key?
At the moment, no. We are thinking about implementing this feature in a
future release. If you think this feature would be useful for your
application, please let us know by e-mailing suggest@tacosoftware.com.
Which properties use delayed values?
This information is intentionally hidden from the user to allow freedom
in implementation, but I will say that the values property of JTable does use delayed values, so
you might want to use an instance of DelayedResultObservableMap
if you listen for changes to table values.
BeanShell Issues
My BeanShell scripts aren't working!
Why not?
First of all, ensure that the real BeanShell library (not the facade
version) is in your classpath during execution. To test for this, you
can try java bsh.Interpreter
from the command-line.
Are you having trouble with particular scripts? BeanShell is sensitive
to the current directory when importing
classes. It might not be able to import a class if you run the
application in a directory containing .class files with the same name.
Therefore you should debug and run your application either from the
root of the
classpath for your project, or in a directory that contains no .class
files in it or any of its subdirectories.
If you're running an applet, unless your applet is signed and the user
accepts weaker security, all BeanShell scripts are pretty much doomed
to fail. Avoid BeanShell when developing applets. You should use the
facade version of the BeanShell library, which simply throws exceptions
when requests are made to interpret BeanShell scripts, because you
won't be able to use BeanShell scripts anyway, and the facade version
is much smaller.