All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.cuf.ui.builder.SwingXMLBuilder Maven / Gradle / Ivy

The newest version!
/**************************************************************************
 *  P A C K A G E                                                         *
 **************************************************************************/

package net.sf.cuf.ui.builder;

/**************************************************************************/

/**************************************************************************
 *  I M P O R T S                                                         *
 **************************************************************************/

import org.jdom2.input.SAXBuilder;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.Attribute;
import org.jdom2.Parent;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;

import javax.swing.JComponent;
import java.io.InputStream;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.StringTokenizer;
import java.util.Locale;
import java.util.ResourceBundle;
import java.awt.Container;
import java.awt.Component;

import net.sf.cuf.ui.SwingDecoratorFunctionality;
import net.sf.cuf.state.State;
import net.sf.cuf.model.ValueModel;

/**************************************************************************/

/**************************************************************************
 *  C L A S S E S                                                         *
 **************************************************************************/

/**************************************************************************/

/**
 * SwingXMLBuilder generates a visual representation from a simple XML
 * description.
* It also can create non-visble components, and connect the actions * of the visuable components to any methods of the non-visual objects.
* Besides generating arbitrary objects (as long as the class has a public * default constructor), also state handling and data binding objects can * generated.
* It is somehow like the legendary "NIB-Files" from the NeXT/Apple * InterfaceBuilder.app, unfortunately without the interactive UI Builder :-(
* * The state handling stuff centers around the {@link net.sf.cuf.state.State} * and {@link net.sf.cuf.state.StateAdapter} interfaces, and can be used * to describe logical states and bind them to the widgets.
* * The data binding stuff centers around the {@link net.sf.cuf.model.ValueModel} * interface, and can be used to bind the content of POJO's to widgets, * even if the POJO's contain complex lists or a type conversion is required.
* * Both visual widgets as well as non-visual objects can be referred by name, * see SwingMapping for a detailed description of how this works.
* The "id" Attribute of the various elements are mapped to the name of a * widget, see JComponent.setName() for further information.
* * This class is somehow an alternative to SwingMapping+SwingDecorator, * because the XML description contains all the information a SwingDecorator * would normally decorate, and a SwingXMLBuilder has the same * key/value methods like SwingMapping.
* * The non-visual objects only have a flat (non-hierarchical) namespace. * It would be very easy to fix that, but because there is no generic * "add to parent" method, it generates little value.
* If the non-visual object implements the SwingXMLBuilder.Backlink interface, * the "void setSwingXMLBuilder(SwingXMLBuilder pBuilder)" * method is called with from the builder that created the object.
* * FIXME: Add support for SpringLayout
* FIXME: All layout constants should have symbolic names
* FIXME: Add depending filling to the popup menu support, and allow more than * one popup per widget
* * @author Jürgen Zeller, [email protected] */ public class SwingXMLBuilder { /** our DTD SYSTEM ID for version 1.4 */ public static final String SYSTEM_ID_1_4 = "http://www.sdm.com/dtd/xml2swing-1.4.dtd"; /** file where expect the DTD for version 1.4 */ private static final String SYSTEM_ID_1_4_FILE = "xml2swing-1.4.dtd"; /** our DTD SYSTEM ID for version 1.5 */ public static final String SYSTEM_ID_1_5 = "http://www.sdm.com/dtd/xml2swing-1.5.dtd"; /** file where expect the DTD for version 1.5 */ private static final String SYSTEM_ID_1_5_FILE = "xml2swing-1.5.dtd"; /** our DTD SYSTEM ID for version 1.6 */ public static final String SYSTEM_ID_1_6 = "http://www.sdm.com/dtd/xml2swing-1.6.dtd"; /** file where expect the DTD for version 1.6 */ private static final String SYSTEM_ID_1_6_FILE = "xml2swing-1.6.dtd"; /** our DTD SYSTEM ID for version 1.7 */ public static final String SYSTEM_ID_1_7 = "http://www.sdm.com/dtd/xml2swing-1.7.dtd"; /** file where expect the DTD for version 1.7 */ private static final String SYSTEM_ID_1_7_FILE = "xml2swing-1.7.dtd"; /** our DTD SYSTEM ID for version 1.8 */ public static final String SYSTEM_ID_1_8 = "http://www.sdm.com/dtd/xml2swing-1.8.dtd"; /** file where expect the DTD for version 1.8 */ private static final String SYSTEM_ID_1_8_FILE = "xml2swing-1.8.dtd"; /** our DTD SYSTEM ID for version 1.9 */ public static final String SYSTEM_ID_1_9 = "http://www.sdm.com/dtd/xml2swing-1.9.dtd"; /** file where expect the DTD for version 1.9 */ private static final String SYSTEM_ID_1_9_FILE = "xml2swing-1.9.dtd"; /** our DTD SYSTEM ID for version 2.0 */ public static final String SYSTEM_ID_2_0 = "http://www.sdm.com/dtd/xml2swing-2.0.dtd"; /** file where expect the DTD for version 2.0 */ private static final String SYSTEM_ID_2_0_FILE = "xml2swing-2.0.dtd"; /** our DTD SYSTEM ID for version 2.1 */ public static final String SYSTEM_ID_2_1 = "http://www.sdm.com/dtd/xml2swing-2.1.dtd"; /** file where expect the DTD for version 2.1 */ private static final String SYSTEM_ID_2_1_FILE = "xml2swing-2.1.dtd"; /** flag if we validate XML input (false: no validation, faster!) */ private static final boolean XML_VALIDATE = true; /** * Registry to be used for the widget creation, also the factory to be used for creation */ private static WidgetFactoryRegistry sWidgetFactoryRegistry; /** separator of component names */ public static final String SEPARATOR = "/"; // non-visual elements public static final String OBJECT_ELEMENT = "object"; public static final String ACTION_ELEMENT = "action"; public static final String RESOURCE_ELEMENT = "resource"; // action specific elements public static final String IS_TOGGLE_ELEMENT = "isToggle"; /** Default id for loaded resources. */ public static final String DEFAULT_ID_RESOURCE = "ResourceBundle"; // widgets public static final String FRAME_ELEMENT = "frame"; public static final String DIALOG_ELEMENT = "dialog"; public static final String PANEL_ELEMENT = "panel"; public static final String ANY_ELEMENT = "any"; public static final String BUTTON_ELEMENT = "button"; public static final String LABEL_ELEMENT = "label"; public static final String RADIOBUTTON_ELEMENT = "radiobutton"; public static final String TOGGLEBUTTON_ELEMENT = "togglebutton"; public static final String CHECKBOX_ELEMENT = "checkbox"; public static final String TEXTFIELD_ELEMENT = "textfield"; public static final String PASSWORDFIELD_ELEMENT = "passwordfield"; public static final String TEXTAREA_ELEMENT = "textarea"; public static final String COMBOBOX_ELEMENT = "combobox"; public static final String SLIDER_ELEMENT = "slider"; public static final String SPLITPANE_ELEMENT = "splitpane"; public static final String SCROLLPANE_ELEMENT = "scrollpane"; public static final String TABBEDPANE_ELEMENT = "tabbedpane"; public static final String LIST_ELEMENT = "list"; public static final String TABLE_ELEMENT = "table"; public static final String TREE_ELEMENT = "tree"; public static final String SEPARATOR_ELEMENT = "separator"; public static final String SEPARATORPANEL_ELEMENT= "separatorpanel"; public static final String MENUBAR_ELEMENT = "menubar"; public static final String MENU_ELEMENT = "menu"; public static final String MENU_ITEM_ELEMENT = "menuitem"; public static final String TOOLBAR_ELEMENT = "toolbar"; public static final String TOOLBAR_ITEM_ELEMENT = "toolbaritem"; public static final String POPUP_ELEMENT = "popup"; public static final String POPUPMENU_ELEMENT = "popupmenu"; public static final String POPUP_ITEM_ELEMENT = "popupitem"; public static final String BUILDER_ELEMENT = "builder"; public static final String SPINNER_ELEMENT = "spinner"; // decorations public static final String PROPERTY_ELEMENT = "property"; public static final String TEXT_ELEMENT = "text"; public static final String TITLE_ELEMENT = "title"; public static final String MNEMONIC_ELEMENT = "mnemonic"; public static final String SHORTCUT_ELEMENT = "shortcut"; public static final String ACCELERATOR_ELEMENT = "accelerator"; public static final String TOOLTIP_ELEMENT = "tooltip"; public static final String ICON_ELEMENT = "icon"; public static final String COMBOITEM_ELEMENT = "comboitem"; public static final String TABMAPPING_ELEMENT = "tabmapping"; public static final String TABITEM_ELEMENT = "tabitem"; public static final String POPUPREF_ELEMENT = "popupref"; public static final String LAYOUTMANAGER_ELEMENT = "layoutmanager"; public static final String CONSTRAINT_ELEMENT = "constraint"; public static final String BORDER_ELEMENT = "border"; public static final String DLU_ELEMENT = "dlu"; public static final String MINSIZE_ELEMENT = "minsize"; public static final String MAXSIZE_ELEMENT = "maxsize"; public static final String PREFSIZE_ELEMENT = "prefsize"; // connections public static final String BUTTONACTION_ELEMENT = "buttonAction"; public static final String COMBOBOXACTION_ELEMENT = "comboboxAction"; public static final String TEXTFIELDACTION_ELEMENT = "textfieldAction"; public static final String MENUACTION_ELEMENT = "menuAction"; public static final String ACTIONACTION_ELEMENT = "actionAction"; public static final String CARETACTION_ELEMENT = "caretAction"; public static final String CHANGEACTION_ELEMENT = "changeAction"; public static final String FOCUSACTION_ELEMENT = "focusAction"; public static final String LISTSELECTIONACTION_ELEMENT = "listSelectionAction"; public static final String PROPERTYCHANGEACTION_ELEMENT = "propertyChangeAction"; public static final String TREESELECTIONACTION_ELEMENT = "treeSelectionAction"; public static final String DOCUMENTACTION_ELEMENT = "documentAction"; // state handling public static final String STATE_ELEMENT = "state"; public static final String EXPRESSION_ELEMENT = "expression"; public static final String STATEADAPTER_ELEMENT = "stateAdapter"; public static final String SETSTATE_ELEMENT = "setstate"; public static final String ADAPTEE_ELEMENT = "adaptee"; // data binding public static final String VALUEHOLDER_ELEMENT = "valueholder"; public static final String BUFFEREDHOLDER_ELEMENT = "bufferedholder"; public static final String ASPECTADAPTER_ELEMENT = "aspectadapter"; public static final String INDEXEDADAPTER_ELEMENT = "indexedadapter"; public static final String INDEXINLIST_ELEMENT = "indexinlist"; public static final String PROPERTIESADAPTER_ELEMENT = "propertiesadapter"; public static final String TYPECONVERTER_ELEMENT = "typeconverter"; public static final String FORMATCONVERTER_ELEMENT = "formatconverter"; public static final String REGEXPCONVERTER_ELEMENT = "regexpconverter"; public static final String CONVERTERINSYNC_ELEMENT = "converterinsync"; public static final String SELECTIONINLIST_ELEMENT = "selectioninlist"; public static final String MULTISELECTIONINLIST_ELEMENT = "multiselectioninlist"; public static final String SETVALUE_ELEMENT = "setvalue"; public static final String DOCUMENTADAPTER_ELEMENT = "documentadapter"; public static final String BUTTONADAPTER_ELEMENT = "buttonadapter"; public static final String COMBOBOXADAPTER_ELEMENT = "comboboxadapter"; public static final String TABLEADAPTER_ELEMENT = "tableadapter"; public static final String LISTADAPTER_ELEMENT = "listadapter"; public static final String FILTERINGLISTADAPTER_ELEMENT = "filteringlistadapter"; public static final String COLUMN_ELEMENT = "column"; public static final String LOVMAPPER_ELEMENT = "lovmapper"; public static final String TOOLTIPADAPTER_ELEMENT = "tooltipadapter"; public static final String LABELADAPTER_ELEMENT = "labeladapter"; public static final String LOVADAPTER_ELEMENT = "lovadapter"; // attributes public static final String KEY_ATTRIBUTE = "key"; public static final String VALUE_ATTRIBUTE = "value"; public static final String BOOLEAN_ATTRIBUTE = "boolean"; public static final String INT_ATTRIBUTE = "int"; public static final String REF_ATTRIBUTE = "ref"; public static final String REF2_ATTRIBUTE = "ref2"; public static final String ID_ATTRIBUTE = "id"; public static final String MIN_ATTRIBUTE = "min"; public static final String MAX_ATTRIBUTE = "max"; public static final String ACTIONREF_ATTRIBUTE = "actionref"; public static final String CLOSEACTION_ATTRIBUTE = "closeAction"; public static final String ESCAPEACTION_ATTRIBUTE= "escapeAction"; public static final String MODAL_ATTRIBUTE = "modal"; public static final String CLASS_ATTRIBUTE = "class"; public static final String FILTER_CLASS_ATTRIBUTE = "filterClass"; public static final String CONSTANT_ATTRIBUTE = "constant"; public static final String BASENAME_ATTRIBUTE = "basename"; public static final String DEFAULT_ATTRIBUTE = "default"; public static final String LANG_ATTRIBUTE = "lang"; public static final String TYPE_ATTRIBUTE = "type"; public static final String NAME_ATTRIBUTE = "name"; public static final String ORIENTATION_ATTRIBUTE = "orientation"; public static final String ONETOUCHEXPANDABLE_ATTRIBUTE= "oneTouchExpandable"; public static final String TABPLACEMENT_ATTRIBUTE= "tabPlacement"; public static final String SOURCE_ATTRIBUTE = "source"; public static final String TARGET_ATTRIBUTE = "target"; public static final String METHOD_ATTRIBUTE = "method"; public static final String ENABLED_ATTRIBUTE = "enabled"; public static final String SELECTED_ATTRIBUTE = "selected"; public static final String VERTICAL_SCROLLBAR_ATTRIBUTE = "verticalScrollBar"; public static final String HORIZONTAL_SCROLLBAR_ATTRIBUTE = "horizontalScrollBar"; public static final String ALIGN_ATTRIBUTE = "align"; public static final String LABELFOR_ATTRIBUTE = "labelfor"; public static final String COMMAND_ATTRIBUTE = "command"; public static final String DOCUMENT_ATTRIBUTE = "document"; public static final String COLUMNS_ATTRIBUTE = "columns"; public static final String MASKFORMAT_ATTRIBUTE = "maskFormat"; public static final String DATEFORMAT_ATTRIBUTE = "dateFormat"; public static final String NUMBERFORMAT_ATTRIBUTE= "numberFormat"; public static final String EDITABLE_ATTRIBUTE = "editable"; public static final String LINEWRAP_ATTRIBUTE = "linewrap"; public static final String INVERT_ATTRIBUTE = "invert"; public static final String SUBJECT_ATTRIBUTE = "subject"; public static final String TRIGGER_ATTRIBUTE = "trigger"; public static final String DEEPCOPY_ATTRIBUTE = "deepcopy"; public static final String SETPREFIX_ATTRIBUTE = "setprefix"; public static final String GETPREFIX_ATTRIBUTE = "getprefix"; public static final String ACCESS_ATTRIBUTE = "access"; public static final String ACCESSREF_ATTRIBUTE = "accessref"; public static final String MODELREF_ATTRIBUTE = "modelref"; public static final String PREF_WIDTH_ATTRIBUTE = "prefwidth"; public static final String WIDGETREF_ATTRIBUTE = "widgetref"; public static final String FORMAT_ATTRIBUTE = "format"; public static final String BLOCK_ATTRIBUTE = "block"; public static final String MODIFIERS_ATTRIBUTE = "modifiers"; public static final String CONDITION_ATTRIBUTE = "condition"; public static final String SORTABLE_ATTRIBUTE = "sortable"; public static final String SORTMODELREF_ATTRIBUTE= "sortmodelref"; public static final String COMPARATORCLASS_ATTRIBUTE= "comparatorclass"; public static final String INITIAL_SORTING_COLUMN_ATTRIBUTE = "initialsortingcolumn"; public static final String DEEP_ATTRIBUTE = "deep"; public static final String OPAQUE_ATTRIBUTE = "opaque"; public static final String LOVSREF_ATTRIBUTE = "lovsref"; public static final String ITEMSREF_ATTRIBUTE = "itemsref"; public static final String KEYSREF_ATTRIBUTE = "keysref"; public static final String ITEMS_ATTRIBUTE = "items"; public static final String KEYS_ATTRIBUTE = "keys"; public static final String UNSELECTED_ATTRIBUTE = "unselected"; public static final String IMPORTNAMES_ATTRIBUTE = "importNames"; public static final String EXT_UPD_REF_ATTRIBUTE = "extUpdRef"; // to make the handling of jgoodies form builders easy, we store the // builder in the properties of the component it operates on public static final String FORMBUILDER_PROPERTY = "formbuilder"; // to make the creation of radio buttons easy, we store temporarly // a hashmap for the tab-mapping in the JTabbedPane properties public static final String TABMAPPING_PROPERTY = "tabmapping"; // we store the "ESCAPE"-Action in the JFrame/JDialog under that name public static final String CANCEL_ACTION_KEY = "CANCEL_ACTION_KEY"; // to make the creation of radio buttons easy, we store the ButtonGroup // in their parents properties public static final String BUTTONGROUP_PROPERTY = "buttongroup"; // marker for radio buttons to preselect the pressed one public static final String BUTTONGROUP_TRUE = "true"; // values for the layoutmanager and constraint type attribute and other elements public static final String GRIDBAG_LAYOUT = "gridbag"; public static final String NULL_LAYOUT = "null"; public static final String BORDER_LAYOUT = "border"; public static final String BOX_LAYOUT = "box"; public static final String CARD_LAYOUT = "card"; public static final String FLOW_LAYOUT = "flow"; public static final String GRID_LAYOUT = "grid"; public static final String TABLE_LAYOUT = "table"; public static final String FORM_LAYOUT = "form"; public static final String FLEXIBLEGRID_LAYOUT = "flexiblegrid"; public static final String HGAP_ELEMENT = "hgap"; public static final String VGAP_ELEMENT = "vgap"; public static final String AXIS_ELEMENT = "axis"; public static final String ALIGN_ELEMENT = "align"; public static final String COLUMNS_ELEMENT = "columns"; public static final String ROWS_ELEMENT = "rows"; public static final String COLUMNGROUPS_ELEMENT = "columngroups"; public static final String ROWGROUPS_ELEMENT = "rowgroups"; public static final String LINEBREAK_ELEMENT = "linebreak"; public static final String TABLELAYOUT_FILL = "fill"; public static final String TABLELAYOUT_PREFERRED = "preferred"; public static final String TABLELAYOUT_MINIMUM = "minimum"; // values for the border type attribute and elements public static final String LINE_ATTRIBUTE = "line"; public static final String ETCHED_ATTRIBUTE = "etched"; public static final String BEVEL_ATTRIBUTE = "bevel"; public static final String EMPTY_ATTRIBUTE = "empty"; public static final String MATTE_ATTRIBUTE = "matte"; public static final String COLOR_ELEMENT = "color"; public static final String THICKNESS_ELEMENT = "thickness"; public static final String SUNKEN_ATTRIBUTE = "sunken"; public static final String SUNKEN_RAISED = "raised"; public static final String SUNKEN_LOWERED = "lowered"; public static final String SCROLLBAR_NEVER = "never"; public static final String SCROLLBAR_ALWAYS = "always"; public static final String SCROLLBAR_AS_NEEDED = "asNeeded"; // XML root element public static final String ROOT_ELEMENT = "xml2swing"; // XML non-visual element, first under the ROOT_ELEMENT, optional public static final String NONVISUAL_ELEMENT = "nonvisual"; // XML visual element, second under the ROOT_ELEMENT, optional public static final String VISUAL_ELEMENT = "visual"; // XML connect element, third under the ROOT_ELEMENT, optional public static final String CONNECT_ELEMENT = "connect"; // XML statehandling element, forth under the ROOT_ELEMENT, optional public static final String STATEHANDLING_ELEMENT = "statehandling"; // XML databinding element, fifth under the ROOT_ELEMENT, optional public static final String DATABINDING_ELEMENT = "databinding"; /* * start of non-static members */ /** map, key= hierarchical name, value= JComponent/JFrame/JDialog */ private Map mNameToVisual; /** map, key= short name, value= JComponent/JFrame/JDialog */ private Map mShortNameToVisual; /** map, key= JComponent/JFrame/JDialog, value= (hierarchical) name */ private Map mVisualToName; /** map, key= name, value= non-visual object */ private Map mNameToNonVisual; /** cache for our icons */ private IconCache mIconCache; /** A decorator for widgets and actions. The decorator is created lazily if a ResourceBundle is found. */ private SwingDecoratorFunctionality mSwingDecorator; /** Indicator if we already tried to create the {@link #mSwingDecorator}. */ private boolean mSwingDecoratorCreated; /** Locale used for this instance of the SwingXMLBuilder. Is set to {@link Locale#getDefault} upon creation. */ private Locale mLocale; /** Marker if the SwingXMLBuilder was disposed and shouldn't be used any longer. */ private boolean mIsDisposed; /** * Determine a decorator for widgets and actions. The decorator is * instantiated lazily upon the first call to this method (which happens * implicitely when actions or visuals are created). To be constructed it * needs one or more non-visual {@link ResourceBundle} whose ids begin with * {@link #DEFAULT_ID_RESOURCE}. Otherwise this method returns * null and no other attempt is made to construct a decorator. * @return decorator or null */ public SwingDecoratorFunctionality getSwingDecorator() { if ( !mSwingDecoratorCreated ) { mSwingDecoratorCreated = true; // look for ResourceBundles with the right id List rbs = new ArrayList<>(); for (final Object o : getNameToNonVisual().keySet()) { String name = (String) o; if (name != null && name.startsWith(DEFAULT_ID_RESOURCE)) { ResourceBundle rb = (ResourceBundle) getNonVisualObject(name); // check for correct type rbs.add(rb); } } // instantiate the decorator if we found any if (!rbs.isEmpty()) { mSwingDecorator = new SwingDecoratorFunctionality(false, getIconCache()); for (final Object rb : rbs) { mSwingDecorator.addBundle((ResourceBundle) rb); } } } return mSwingDecorator; } /** * Determine the locale used for this instance of the SwingXMLBuilder. * @return used locale */ public Locale getLocale() { return mLocale; } /** * widget registry stuff * @return the widget factory, never null */ public static WidgetFactoryRegistry getWidgetFactoryRegistry() { if (sWidgetFactoryRegistry==null) { // initializes the WidgetFactoryRegistry sWidgetFactoryRegistry= new WidgetFactoryRegistry(); } return sWidgetFactoryRegistry; } /* * create stuff */ /** * SwingXMLBuilder has a private constructor, because instances are created * with the create() methods. * @param pLocale locale to use or null if the current default locale is to be used */ private SwingXMLBuilder(Locale pLocale) { if (pLocale == null) { pLocale = Locale.getDefault(); } mNameToVisual = new HashMap<>(); mShortNameToVisual= new HashMap<>(); mVisualToName = new HashMap<>(); mNameToNonVisual = new HashMap<>(); mIconCache = new IconCache(); mLocale = pLocale; } /** * Create visual, non-visual objects and a connection between them. * * @param pElement start point ("root") of the process * @throws IllegalArgumentException if pElement is null, or the XML file contains errors * @return a SwingXMLBuilder */ public static SwingXMLBuilder create(final Element pElement) throws IllegalArgumentException { return create(pElement, null, null); } /** * Create visual, non-visual objects and a connection between them. * * @param pElement start point ("root") of the process * @param pLocale locale to use or null if the current default locale is to be used * @throws IllegalArgumentException if pElement is null, or the XML file contains errors * @return a SwingXMLBuilder */ public static SwingXMLBuilder create(final Element pElement, final Locale pLocale) throws IllegalArgumentException { return create(pElement, pLocale, null); } /** * Create visual, non-visual objects and a connection between them. * * @param pElement start point ("root") of the process * @param pNonVisual null or non-visual key/object mapping * @throws IllegalArgumentException if pElement is null, or the XML file contains errors * @return a SwingXMLBuilder */ public static SwingXMLBuilder create(final Element pElement, final Map pNonVisual) throws IllegalArgumentException { return create(pElement, null, pNonVisual); } /** * Create visual, non-visual objects and a connection between them. * * @param pElement start point ("root") of the process * @param pLocale locale to use or null if the current default locale is to be used * @param pNonVisual null or non-visual key/object mapping * @throws IllegalArgumentException if pElement is null, or the XML file contains errors * @return a SwingXMLBuilder */ public static SwingXMLBuilder create(final Element pElement, final Locale pLocale, final Map pNonVisual) throws IllegalArgumentException { if (pElement==null) throw createException("null element"); if (!ROOT_ELEMENT.equals(pElement.getName())) throw createException("XML root element ist not "+ROOT_ELEMENT, pElement); SwingXMLBuilder builder= new SwingXMLBuilder(pLocale); BuilderDelegate delegate; // create non-visual stuff, first add the external non visual objects if (pNonVisual!=null) { // check if all keys are strings for (final Object o : pNonVisual.keySet()) { if (!(o instanceof String)) { if (o == null) throw createException("key of the pNonVisual Map is not a string but null", pElement); else throw createException("key of the pNonVisual Map is not a string but a " + o.getClass().getName(), pElement); } } // merge builder.mNameToNonVisual.putAll(pNonVisual); } Element nonVisualElement= pElement.getChild(NONVISUAL_ELEMENT); if (nonVisualElement!=null) { delegate= new NonVisualBuilderDelegate(); delegate.build(builder, nonVisualElement); } // create visual stuff second so we are sure that all actions are parsed Element visualElement= pElement.getChild(VISUAL_ELEMENT); if (visualElement!=null) { delegate= new VisualBuilderDelegate(); delegate.build(builder, visualElement); } // handle connnections Element connectElement = pElement.getChild(CONNECT_ELEMENT); if (connectElement!=null) { delegate= new ConnectionBuilderDelegate(); delegate.build(builder, connectElement); } // handle ValueModels Element dataBindingElement= pElement.getChild(DATABINDING_ELEMENT); if (dataBindingElement!=null) { delegate= new DataBindingBuilderDelegate(); delegate.build(builder, dataBindingElement); } // handle states Element stateElement= pElement.getChild(STATEHANDLING_ELEMENT); if (stateElement!=null) { delegate= new StateHandlingBuilderDelegate(); delegate.build(builder, stateElement); } return builder; } /** * Create visual, non-visual objects and a connection between them. * * @param pInputStream the XML description * @throws IllegalArgumentException if there are problems with pInputStream, * or the XML file contains errors * @return a SwingXMLBuilder */ public static SwingXMLBuilder create(final InputStream pInputStream) throws IllegalArgumentException { return create(pInputStream, null, null); } /** * Create visual, non-visual objects and a connection between them. * * @param pInputStream the XML description * @param pLocale locale to use or null if the current default locale is to be used * @throws IllegalArgumentException if there are problems with pInputStream, * or the XML file contains errors * @return a SwingXMLBuilder */ public static SwingXMLBuilder create(final InputStream pInputStream, final Locale pLocale) throws IllegalArgumentException { return create(pInputStream, pLocale, null); } /** * Create visual, non-visual objects and a connection between them. * * @param pInputStream the XML description * @param pNonVisual null or non-visual key/object mapping * @throws IllegalArgumentException if there are problems with pInputStream, * or the XML file contains errors * @return a SwingXMLBuilder */ public static SwingXMLBuilder create(final InputStream pInputStream, final Map pNonVisual) throws IllegalArgumentException { return create(pInputStream, null, pNonVisual); } /** * Create visual, non-visual objects and a connection between them. * @param pInputStream the XML description * @param pLocale locale to use or null if the current * default locale is to be used * @param pNonVisual null or non-visual key/object mapping * @return a SwingXMLBuilder * @throws IllegalArgumentException if there are problems with pInputStream, * or the XML file contains errors */ public static SwingXMLBuilder create(final InputStream pInputStream, final Locale pLocale, final Map pNonVisual) throws IllegalArgumentException { if (pInputStream==null) throw createException("null inputstream"); SAXBuilder builder= new SAXBuilder(); builder.setEntityResolver(new EntityHelper()); if (XML_VALIDATE) { builder.setValidation(true); } try { Document doc = builder.build(pInputStream); Element root= doc.getRootElement(); return create(root, pLocale, pNonVisual); } catch (Exception e) { throw createException("could not parse document", e); } } /** * This will cause all internal memory to be cleared, useful to * prevent GC problems. We also dispose all known State's and ValueModel's
* Warning: no other method can be called after calling dispose! */ public void dispose() { if (!mIsDisposed) { for (final Object o1 : mNameToNonVisual.values()) { if (o1 instanceof State) { State state = (State) o1; if (!state.isDisposed()) { state.dispose(); } } else if (o1 instanceof ValueModel) { ValueModel valueModel = (ValueModel) o1; if (!valueModel.isDisposed()) { valueModel.dispose(); } } } mIconCache= null; mLocale= null; mNameToNonVisual.clear(); mNameToNonVisual= null; mNameToVisual.clear(); mNameToVisual= null; mShortNameToVisual.clear(); mShortNameToVisual= null; mSwingDecorator= null; mSwingDecoratorCreated= false; mVisualToName.clear(); mVisualToName= null; mIsDisposed= true; } } /* * bulk access stuff of our known artefacts */ /** * Return the hierarchical name to visual mapping. * @return the name (key) to visual (value) map */ public Map getNameToVisual() { return mNameToVisual; } /** * Return the short name to visual mapping. * @return the name (key) to visual (value) map */ public Map getShortNameToVisual() { return mShortNameToVisual; } /** * Return the visual to hierarchical name mapping. * @return the visual (key) to the hierarchical name (value) map */ public Map getVisualToName() { return mVisualToName; } /** * Return the name to non-visual mapping. * @return the name (key) to non-visual (value) map */ public Map getNameToNonVisual() { return mNameToNonVisual; } /* * single access stuff */ /** * Returns a widget for the given name. * * @param pName (hierarchical) name of the widget * @return null if the object is unknown */ public Container getContainerByName(final String pName) { if (mIsDisposed) throw new IllegalStateException("SwingXMLBuilder was disposed"); Object value= getVisualByName(pName); if (value instanceof Container) return (Container)value; else return null; } /** * Returns (hierarchical) name for the given widget. * * @param pContainer a widget * @return null if the widget is unknown, (hierarchical) name otherwise */ public String getNameByContainer(final Container pContainer) { if (mIsDisposed) throw new IllegalStateException("SwingXMLBuilder was disposed"); return mVisualToName.get(pContainer); } /** * Returns a widget for the given name, shortcut for getComponentByName * * @param pName (hierarchical) name of the widget * @return null if the object is unknown, or the widget is not a JComponent * @throws IllegalArgumentException the name is not resolvable due too many ".."'s */ public JComponent get(final String pName) { return getComponentByName(pName); } /** * Returns a widget for the given short name. If there is more than * one component with that short name, it is undefined which is return. * @param pName short name of the widget * @return null if the object is unknown, or the widget is not a JComponent */ public JComponent getByShortName(final String pName) { if (mIsDisposed) throw new IllegalStateException("SwingXMLBuilder was disposed"); Object value= mShortNameToVisual.get(pName); if (value instanceof JComponent) return (JComponent)value; else return null; } /** * Returns a widget for the given name. * @param pName (hierarchical) name of the widget * @return null if the object is unknown, or the widget is not a JComponent * @throws IllegalArgumentException the name is not resolvable due too many ".."'s */ public JComponent getComponentByName(final String pName) { if (mIsDisposed) throw new IllegalStateException("SwingXMLBuilder was disposed"); Object value= getVisualByName(pName); if (value instanceof JComponent) return (JComponent)value; else return null; } /** * Returns a widget by name. First the hierarchical name is * tried, than the short name * @param pName the hierarchical or shor name * @return null if the object is unknown, or the widget is not a JComponent * @throws IllegalArgumentException the name is not resolvable due too many ".."'s */ public JComponent getComponentByAnyName(final String pName) { JComponent back= getComponentByName(pName); if (back==null) { back= getByShortName(pName); } return back; } /** * common part of getContainerByName() and getComponentByName(), does also * the ".." resolving * @param pName component name (may be null), inside the * name all ".."'s are resolved first, so it is o.k. to * use "somePanel/someButton/../someTextField" to get * from any component to any other. * @return null or the object from mNameToVisual that matches the (resolved) name */ private Object getVisualByName(final String pName) { // null maps always to null if (pName==null) { return null; } String name; // check if we need the ".." resolving at all if (!pName.contains("..")) { name= pName; } else { // the following code does the ".." resolving StringTokenizer tokenizer = new StringTokenizer(pName, SEPARATOR); List list = new ArrayList<>(tokenizer.countTokens()); int stackCount= 0; while (tokenizer.hasMoreTokens()) { String token= tokenizer.nextToken(); if ("..".equals(token)) { // pop stackCount--; if (stackCount<0) { throw createException(pName + ": contains more ..'s than elements"); } list.remove(stackCount); } else { // push list.add(token); stackCount++; } } StringBuilder nameBuffer = new StringBuilder(); for (int i= 0, n= list.size(); i < n; i++) { nameBuffer.append(list.get(i)); if (i < n-1) { nameBuffer.append(SEPARATOR); } } name= nameBuffer.toString(); } // name now contains the resolved name, the Map will either return // null or the matching object return mNameToVisual.get(name); } /** * Returns (hierarchical) name for the given widget. * * @param pComponent a widget * @return null if the widget is unknown, (hierarchical) name otherwise */ public String getNameByComponent(final JComponent pComponent) { return getNameByContainer(pComponent); } /** * Returns a non-visual object that was in the description. * * @param pName (hierarchical) name of the non-visual object, may be null * @return null if the object is unknown */ public Object getNonVisualObject(final String pName) { if (mIsDisposed) throw new IllegalStateException("SwingXMLBuilder was disposed"); return mNameToNonVisual.get(pName); } /** * Returns a non-visual object that was in the description. * * @param pName (hierarchical) name of the non-visual object, may be null * @param pType the type we should return, must not be null * @param the type we expect * @return null if the object is unknown */ public T getNonVisualObject(final String pName, Class pType) { if (mIsDisposed) throw new IllegalStateException("SwingXMLBuilder was disposed"); return pType.cast(mNameToNonVisual.get(pName)); } /** * Return the icon cache of this builder. * @return the icon cache of this builder */ public IconCache getIconCache() { if (mIsDisposed) throw new IllegalStateException("SwingXMLBuilder was disposed"); return mIconCache; } /** * Returns the widget factory of this builder. There is no setWidgetFactory() * method, because it makes little sense to change the widget factory after * all widgets are created. * @return the widget factory of this builder */ public WidgetFactory getWidgetFactory() { if (mIsDisposed) throw new IllegalStateException("SwingXMLBuilder was disposed"); return getWidgetFactoryRegistry(); } /** * Small helper method to provide better exception handling by formatting * the message. * * @param pMessagePrefix null or the initial part of the message * @return IllegalArgumentException with a message and e as its cause */ public static IllegalArgumentException createException(final String pMessagePrefix) { return createException(pMessagePrefix, null, null); } /** * Small helper method to provide better exception handling by formatting * a message that includes the original exception. * * @param pException null or the exception that triggered the call * @param pMessagePrefix null or the initial part of the message * @return IllegalArgumentException with a message and e as its cause */ public static IllegalArgumentException createException( final String pMessagePrefix, final Exception pException) { return createException(pMessagePrefix, pException, null); } /** * Small helper method to provide better exception handling by formatting * a message that includes the place where the error happend. * * @param pMessagePrefix null or the initial part of the message * @param pElement null or the current element, the element's path will be appended * to the message. * @return IllegalArgumentException with a message and e as its cause */ public static IllegalArgumentException createException( final String pMessagePrefix, final Element pElement) { return createException(pMessagePrefix, null, pElement); } /** * Small helper method to provide better exception handling by formatting * a message that includes the original exception and the * place where the error happend. * * @param pMessagePrefix null or the initial part of the message * @param pException null or the exception that triggered the call * @param pElement null or the current element, the element's path will be appended * to the message. * @return IllegalArgumentException with a message and e as its cause */ public static IllegalArgumentException createException( final String pMessagePrefix, final Exception pException, final Element pElement) { String CR = System.getProperty("line.separator"); int INDENT = 4; String prefix = (pMessagePrefix==null) ? "no detailed message" : pMessagePrefix; StringBuilder sb = new StringBuilder(prefix); if (pElement!=null) { // handle pElement List elementBacktrace= new ArrayList<>(); Parent current = pElement; do { elementBacktrace.add(current); } while ((current= current.getParent()) instanceof Element); sb.append(", XML Element backtrace:"); sb.append(CR); for (int i = 0, n = elementBacktrace.size(); i < n; i++) { Element element= (Element) elementBacktrace.get(n-i-1); String id = element.getAttributeValue(ID_ATTRIBUTE); for (int j= 0; j< i*INDENT; j++) sb.append(' '); sb.append(element.getName()); if (id!=null) { sb.append(" (id="); sb.append(id); sb.append(')'); } else { sb.append(" (id unknown)"); } if (i!=(n-1)) sb.append(CR); } } IllegalArgumentException e= new IllegalArgumentException(sb.toString()); if (pException!=null) { e.initCause(pException); } return e; } /* * helper stuff */ /** * Tests if the element contains information that matches the language of the * current locale. * @param pElement element to check * @param pLocale the locale * @return true if the element has no special language or language matches */ public static boolean isSameLanguage(final Element pElement, final Locale pLocale) { String iso639Language= pLocale.getLanguage(); Attribute elementLanguage= pElement.getAttribute(LANG_ATTRIBUTE); return ((elementLanguage==null) || elementLanguage.getValue().equals(iso639Language)); } /** * Tests if the element contains information that matches the language of the * current locale. * @param pElement element to check * @return true if the element has no special language or language matches */ public boolean isSameLanguage(final Element pElement) { return isSameLanguage(pElement, mLocale); } /** * Returns the text of last title child of the handed element in * the current language or null, if no title child was found. * @param pElement the element we start searching from * @return null or the text of the title element */ public String getTitle(final Element pElement) { String title= null; for (final Object o : pElement.getChildren(TITLE_ELEMENT)) { Element titleElement = (Element) o; if (isSameLanguage(titleElement)) { title = titleElement.getText(); } } return title; } /** * Returns the text of last title child of the handed element in * the current language or null, if no title child was found. * @param pElement the element we start searching from * @return null or the text of the title element */ public static String getTitleDefaultLocale(final Element pElement) { String title= null; for (final Object o : pElement.getChildren(TITLE_ELEMENT)) { Element titleElement = (Element) o; if (isSameLanguage(titleElement, Locale.getDefault())) { title = titleElement.getText(); } } return title; } /** * Get a Class object from the class attribute from pElement. * @param pElement element describing the object * @return null or the Class object * @throws IllegalArgumentException if we have a problem (parameters, ...) */ static Class getClass(final Element pElement) { return getClass(pElement,CLASS_ATTRIBUTE); } /** * Get a Class object from the class attribute from pElement. * @param pElement element describing the object * @param pAttributeName class attribute we are searching for * @return null or the Class object * @throws IllegalArgumentException if we have a problem (parameters, ...) */ static Class getClass(final Element pElement, final String pAttributeName) { String sourceClassName = pElement.getAttributeValue(pAttributeName); Class sourceClass = null; try { if (sourceClassName!=null) { sourceClass = Class.forName(sourceClassName); } } catch (ClassNotFoundException e) { throw createException("class "+sourceClassName+" not found", e, pElement); } return sourceClass; } /** * helper class to resolve our dtd via a local file */ private static class EntityHelper implements EntityResolver { /** * callback method from the XML parser to resolve public/system ID's * @param pPublicId not used * @param pSystemId checked against our SYSTEM_ID member * @return null or a InputSource for pSystemId */ public InputSource resolveEntity (final String pPublicId, final String pSystemId) { switch (pSystemId) { case SYSTEM_ID_2_1: return loadDtd(SYSTEM_ID_2_1_FILE); case SYSTEM_ID_2_0: return loadDtd(SYSTEM_ID_2_0_FILE); case SYSTEM_ID_1_9: return loadDtd(SYSTEM_ID_1_9_FILE); case SYSTEM_ID_1_8: return loadDtd(SYSTEM_ID_1_8_FILE); case SYSTEM_ID_1_7: return loadDtd(SYSTEM_ID_1_7_FILE); case SYSTEM_ID_1_6: return loadDtd(SYSTEM_ID_1_6_FILE); case SYSTEM_ID_1_5: return loadDtd(SYSTEM_ID_1_5_FILE); case SYSTEM_ID_1_4: return loadDtd(SYSTEM_ID_1_4_FILE); } // if not one of our special SYSTEMID's, use the default behaviour return null; } /** * Small helper to load a file * @param pFileName the dtd file name * @return null or the InputSource for the file */ private InputSource loadDtd(final String pFileName) { InputSource back = null; ClassLoader loader= Thread.currentThread().getContextClassLoader(); if (loader!=null) { InputStream dtd= loader.getResourceAsStream(pFileName); if (dtd!=null) { back= new InputSource(dtd); } } return back; } } /** * Helper interface that classes can implement to signal that the * SwingXMLBuilder that created objects of that class will set a back link. */ public interface Backlink { /** * Callback method for the Builder. * @param pBuilder the builder that created that object */ void setSwingXMLBuilder(SwingXMLBuilder pBuilder); } /** * Helper interface that delegate builder classes must implement. */ public interface BuilderDelegate { /** * Build the specific part of the delegate. * @param pBuilder the builder that controls the creation process * @param pElement the JDOM element the delegate should handle */ void build(SwingXMLBuilder pBuilder, Element pElement); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy