/*
 * SSFactory.java  $Revision: 1.52 $  $Date: 2007/10/03 04:39:40 $
 *
 * Copyright (C) 2007 Meldcraft Corporation
 *
 * This file is part of Scaffold.
 * 
 * Scaffold is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Scaffold is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Scaffold.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.meldcraft.application.guis;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Window;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.text.DecimalFormat;
import java.text.Format;
import java.text.MessageFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;

import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.text.*;

import com.meldcraft.application.ApplicationContext;
import com.meldcraft.application.ApplicationElementContext;
import com.meldcraft.application.ManagedElementFactory;
import com.meldcraft.application.guis.mvc.ActionEmitter;
import com.meldcraft.application.guis.mvc.ActionMediator;
import com.meldcraft.application.guis.mvc.ChangeMediator;
import com.meldcraft.application.guis.mvc.DefaultReceiver;
import com.meldcraft.application.guis.mvc.DocumentEmitter;
import com.meldcraft.application.guis.mvc.DocumentMediator;
import com.meldcraft.application.guis.mvc.HyperlinkEmitter;
import com.meldcraft.application.guis.mvc.ItemEmitter;
import com.meldcraft.application.guis.mvc.ItemMediator;
import com.meldcraft.application.guis.mvc.PropertyChangeEmitter;
import com.meldcraft.application.guis.mvc.PropertyChangeMediator;
import com.meldcraft.application.guis.mvc.SelectionEmitter;
import com.meldcraft.application.guis.mvc.SelectionMediator;
import com.meldcraft.application.swing.SwingApplicationContext;
import com.meldcraft.application.swing.action.ActionGUIFactory;
import com.meldcraft.application.util.BeanUtil;
import com.meldcraft.gui.swing.HTMLEditorPane;
import com.meldcraft.gui.swing.MouseMask;
import com.meldcraft.gui.swing.SwingUtil;
import com.meldcraft.util.PropertiesHelper;
import com.meldcraft.util.ReflectUtil;
import com.meldcraft.util.StringUtil;

/**
 * @version $Revision: 1.52 $ $Date: 2007/10/03 04:39:40 $
 * @author Rob Richards
 */
public class SSFactory
{
    protected static final String   ACTION = "action";
    protected static final String   CHANGE = "change";
    protected static final String   DOCUMENT = "document";
    protected static final String   ITEM = "item";
    protected static final String   HYPERLINK = "hyperlink";
    protected static final String   SELECT = "select";
    protected static final String   VALUE = "value";

    protected SSFactory()
    {
    }

    public static Component createInstance(ApplicationElementContext elementContext)
    {
        return createInstance(elementContext, null);
    }

    protected static Component createInstance(ApplicationElementContext elementContext, String type)
    {
        Component ret = null;
        Component retComp = null;

        ApplicationContext context = elementContext.getApplicationContext();
        String elementId = elementContext.getElementId();

        Map config = elementContext.getConfiguration();
        if (type == null)
        {
            type = PropertiesHelper.getString(config, "type", null);
        }
        if (type != null)
        {
            if (type.equals("combobox"))
            {
                DefaultListModel lmodel = createListModel(context, config, elementId);
                DefaultComboBoxModel model = new DefaultComboBoxModel(lmodel.toArray());
                ret = new JComboBox(model);
                ((JComboBox)ret).setSelectedIndex(-1);
            }
            else if (type.equals("dialog"))
            {
                Window parent = SwingUtil.getWindow(((SwingApplicationContext)context).getUIRoot());
                String pstr = PropertiesHelper.getString(config, "parent", null);
                if (pstr != null)
                {
                    Object obj = context.getElementManager().getApplicationElement(pstr);
                    if (obj instanceof Component)
                    {
                        parent = SwingUtil.getWindow((Component)obj);
                    }
                }

                String title = PropertiesHelper.getString(config, "title", null);
                JDialog dialog = new JDialog(parent, title);
                dialog.setModal(PropertiesHelper.getBoolean(config, "modal", true));
                String rootContent = PropertiesHelper.getString(config, "rootContent", null);
                if (rootContent != null)
                {
                    Object obj = context.getElementManager().getApplicationElement(rootContent);
                    if (obj instanceof Component)
                    {
                        dialog.getContentPane().add((Component)obj, BorderLayout.CENTER);
                    }
                }

                SwingUtil.prepareDialog(dialog);

                ret = dialog;
            }
            else if (type.equals("formattedTextField"))
            {
                String fstr = PropertiesHelper.getString(config, "format", "int");
                Format format;
                if (fstr.equals("int"))
                {
                    format = NumberFormat.getIntegerInstance(); 
                }
                else if (fstr.startsWith("date:"))
                {
                    format = new SimpleDateFormat(fstr.substring("date:".length()));
                }
                else if (fstr.startsWith("message:"))
                {
                    format = new MessageFormat(fstr.substring("date:".length()));
                }
                else if (fstr.equals("currency"))
                {
                    format = NumberFormat.getCurrencyInstance();
                }
                else
                {
                    format = new DecimalFormat(fstr);
                }
                JFormattedTextField comp = new JFormattedTextField(format);
                ret = comp;
            }
            else if (type.equals("fieldPane"))
            {
                HashMap over = new HashMap();
                over.put("className", SSFieldPane.class.getName());
                ret = (Component)ManagedElementFactory.getInstance(context).createManagedElement(elementId, null, over);
            }
            else if (type.equals("htmlPane"))
            {
                HTMLEditorPane pane = new HTMLEditorPane();
                boolean open = PropertiesHelper.getBoolean(config, "openOnActivated", true);
                if (!open)
                {
                    pane.removeHyperlinkListener(pane);
                }
                ret = pane;
            }
            else if (type.equals("label"))
            {
                String text = PropertiesHelper.getString(config, "text", null);
                String imgstr = PropertiesHelper.getString(config, "resourceImage", null);
                ImageIcon icon = (imgstr == null) ? null : SwingUtil.loadResourceImage(context.getClass().getClassLoader(), "", imgstr, "");
                JLabel label;
                if (text != null)
                {
                    label= new JLabel(text);
                }
                else
                {
                    if (icon != null)
                    {
                        label = new JLabel(icon);
                        label.setBackground(java.awt.Color.red);
                    }
                    else
                    {
                        label = new JLabel("");
                    }
                }
                ret = label;
            }
            else if (type.equals("list"))
            {
                DefaultListModel model = createListModel(context, config, elementId);
                retComp = new JList(model);
                ret = new JScrollPane((JList)retComp);
            }
            else if (type.equals("mouseMask"))
            {
                String element = PropertiesHelper.getString(config, "maskedElement", null);
                if (element != null)
                {
                    Component comp = getPanelComponent(context, element, config, null);
                    MouseMask mask = new MouseMask(comp);
                    ret = mask;
                }
            }
            else if (type.equals("panel"))
            {
                JPanel panel = new JPanel();
                ret = panel;

                int hgap = 0;
                int vgap = 0;
                String[] gaps = PropertiesHelper.getStringArray(config, "gap");
                if ((gaps != null) && (gaps.length == 2))
                {
                    hgap = Math.max(PropertiesHelper.getInt(gaps[0], 0), 0);
                    vgap = Math.max(PropertiesHelper.getInt(gaps[1], 0), 0);
                }

                String[] rows = PropertiesHelper.getStringArray(config, "elementRows");
                if (rows != null)
                {
                    GridBagLayout gbag = new GridBagLayout();
                    panel.setLayout(gbag);
                    GridBagConstraints gbc = new GridBagConstraints();
                    gbc = new GridBagConstraints();
                    gbc.fill = GridBagConstraints.HORIZONTAL;
                    gbc.anchor = GridBagConstraints.NORTHWEST;
                    gbc.weightx = 0;
                    gbc.weighty = 0;
                    gbc.gridy = 0;

                    for (int i=0 ; i < rows.length ; i++)
                    {
                        if (i > 0)
                        {
                            gbc.insets.top = vgap;
                        }

                        String[] cols = PropertiesHelper.getStringArray(" " + rows[i], "|");
                        if (cols != null)
                        {
                            int nxwidth = 0;
                            gbc.gridx = 0;
                            for (int j=0 ; j < cols.length ; j++)
                            {
                                String col = cols[j];
                                if (col.length() == 0)
                                {
                                    gbc.gridx++;
                                    continue;
                                }

                                if (j == 0)
                                {
                                    gbc.insets.left = 0;
                                }
                                else
                                {
                                    gbc.insets.left = hgap;
                                }

                                int xwidth = nxwidth;
                                nxwidth = 0;
                                int lidx = -1;
                                int ridx = 0;
                                while ((lidx = col.indexOf('<', lidx+1)) > 0)
                                {
                                    ridx = lidx;
                                    xwidth++;
                                }
                                while ((ridx = col.indexOf('>', ridx+1)) > 0)
                                {
                                    nxwidth++;
                                }

                                int idx = col.indexOf('<');
                                if (idx > 0)
                                {
                                    col = col.substring(0, idx).trim();
                                }
                                else if ((idx = col.indexOf('>')) > 0)
                                {
                                    col = col.substring(0, idx).trim();
                                }

                                if (col.equals("^"))
                                {
                                    Component comp = null;
                                    Component[] comps = panel.getComponents();
                                    for (int k=0 ; k < comps.length ; k++)
                                    {
                                        GridBagConstraints c = gbag.getConstraints(comps[k]);
                                        if ((c.gridy < gbc.gridy) && (c.gridx == gbc.gridx))
                                        {
                                            comp = comps[k];
                                        }
                                    }

                                    GridBagConstraints c = gbag.getConstraints(comp);
                                    c.gridheight++;
                                    c.fill = GridBagConstraints.BOTH;
                                    gbag.setConstraints(comp, c);
                                    gbc.gridx += c.gridwidth;

                                }
                                else
                                {
                                    Component comp = getPanelComponent(context, col, config, gbc);
                                    String str = PropertiesHelper.getString(context.getConfiguration(), col + "." + elementId + ".fill", "HORIZONTAL").toUpperCase();
                                    if (str.equals("BOTH"))
                                    {
                                        gbc.fill = GridBagConstraints.BOTH;
                                    }
                                    else if (str.equals("VERTICAL"))
                                    {
                                        gbc.fill = GridBagConstraints.VERTICAL;
                                    }
                                    else if (str.equals("NONE"))
                                    {
                                        gbc.fill = GridBagConstraints.NONE;
                                    }
                                    else
                                    {
                                        gbc.fill = GridBagConstraints.HORIZONTAL;
                                    }

                                    gbc.weightx = PropertiesHelper.getInt(context.getConfiguration(), col + "." + elementId + ".weightx", 0);
                                    gbc.weighty = PropertiesHelper.getInt(context.getConfiguration(), col + "." + elementId + ".weighty", 0);

                                    if (comp != null)
                                    {
                                        gbc.gridwidth = (j == cols.length-1) ? GridBagConstraints.REMAINDER : 1 + xwidth;

                                        gbag.setConstraints(comp, gbc);
                                        panel.add(comp);
                                        gbc.gridx += 1 + xwidth;
                                    }
                                }
                            }
                        }

                        gbc.gridy++;
                    }
                }
                else if ((rows = PropertiesHelper.getStringArray(config, "elements")) != null)
                {
                    panel.setLayout(new BorderLayout());
                    String[] locs = {BorderLayout.CENTER, BorderLayout.NORTH, BorderLayout.EAST, BorderLayout.SOUTH, BorderLayout.WEST};
                    for (int i=0 ; (i < rows.length) && (i < locs.length) ; i++)
                    {
                        String item = rows[i];
                        if (!StringUtil.isEmpty(item))
                        {
                            Component comp = getPanelComponent(context, item, config, null);
                            panel.add(comp, locs[i]);
                        }
                    }
                }
                else if ((rows = PropertiesHelper.getStringArray(config, "elementGrid")) != null)
                {
                    int colcount = PropertiesHelper.getInt(config, "elementGrid.columns", 0);
                    if (colcount != 0)
                    {
                        int rowcount = (int)Math.ceil((double)rows.length / colcount);
                        panel.setLayout(new GridLayout(rowcount, colcount));
                    }
                    for (int i=0 ; i < rows.length ; i++)
                    {
                        String[] cols = PropertiesHelper.getStringArray(" " + rows[i], "|");
                        if (cols != null)
                        {
                            if (colcount == 0)
                            {
                                colcount = cols.length;
                                panel.setLayout(new GridLayout(rows.length, cols.length));
                            }

                            for (int j=0 ; j < cols.length ; j++)
                            {
                                String col = cols[j];
                                Component comp = getPanelComponent(context, col, config, null);
                                panel.add(comp);
                            }
                        }
                    }
                }
                else if ((rows = PropertiesHelper.getStringArray(config, "elementFlow")) != null)
                {
                    panel.setLayout(new FlowLayout());
                    for (int i=0 ; i < rows.length ; i++)
                    {
                        String item = rows[i];
                        Component comp = getPanelComponent(context, item, config, null);
                        panel.add(comp);
                    }
                }
                else if ((rows = PropertiesHelper.getStringArray(config, "elementLayers")) != null)
                {
                    JLayeredPane pane = new JLayeredPane();
                    ret = pane;
                    Dimension size = new Dimension(0,0);
                    for (int i=0 ; i < rows.length ; i++)
                    {
                        String item = rows[i];
                        Component comp = getPanelComponent(context, item, config, null);
                        //((JComponent)comp).setAlignmentX(0.5f);
                        //((JComponent)comp).setAlignmentY(0.5f);
                        //panel.add(comp);
                        Dimension pref = comp.getPreferredSize();
                        size = new Dimension((int)Math.max(size.getWidth(), pref.getWidth()),
                                             (int)Math.max(size.getHeight(), pref.getHeight()));
                        pane.add(comp, JLayeredPane.DEFAULT_LAYER);
                        comp.setSize(pref);
                        pane.setPosition(comp, i);
                    }
                    pane.setPreferredSize(size);
                }
            }
            else if (type.equals("popupMenu"))
            {
                JPopupMenu comp = ActionGUIFactory.getInstance((SwingApplicationContext)context).createJPopupMenu(elementId + ".items");
                ret = comp;
            }
            else if (type.equals("scrollPane"))
            {
                JScrollPane comp = new JScrollPane();
                String viewportView = PropertiesHelper.getString(config, "viewportView", null);
                if (viewportView != null)
                {
                    Object obj = context.getElementManager().getApplicationElement(viewportView);
                    if (obj instanceof Component)
                    {
                        comp.setViewportView((Component)obj);
                    }
                }
                ret = comp;
            }
            else if (type.equals("slider"))
            {
                JSlider comp = new JSlider();
                String[] ticks = PropertiesHelper.getStringArray(config, "ticks");
                if (ticks != null)
                {
                    try
                    {
                        if (ticks.length > 1)
                        {
                            comp.setMinorTickSpacing(new Integer(ticks[0]));
                            comp.setMajorTickSpacing(new Integer(ticks[1]));
                            comp.setPaintTicks(true);
                        }
                        else if (ticks.length > 0)
                        {
                            comp.setMajorTickSpacing(new Integer(ticks[0]));
                            comp.setPaintTicks(true);
                        }
                    }
                    catch (NumberFormatException e)
                    {
                        // ignored
                    }
                }
                String[] labels = PropertiesHelper.getStringArray(config, "labels");
                if (labels != null)
                {
                    Hashtable table = new Hashtable();
                    for (int i=0 ; i < labels.length ; i++)
                    {
                        String val = labels[i];
                        String[] vals = PropertiesHelper.getStringArray(val, "=");
                        if (vals != null)
                        {
                            try
                            {
                                if (vals.length == 1)
                                {
                                    Integer li = new Integer(vals[0]);
                                    JLabel label = new JLabel(vals[0]);
                                    table.put(li, label);
                                }
                                else if (vals.length > 1)
                                {
                                    Integer li = new Integer(vals[0]);
                                    JLabel label = new JLabel(vals[1]);
                                    table.put(li, label);
                                }
                            }
                            catch (NumberFormatException e)
                            {
                                // ignored
                            }
                        }
                    }
                    if (table.size() > 0)
                    {
                        comp.setLabelTable(table);
                        comp.setPaintLabels(true);
                    }
                }

                ret = comp;
            }
            else if (type.equals("spinner"))
            {
                JSpinner comp = new JSpinner();
                /*
                configureComponentProperties(context, elementId, comp);
                int cols = -1;
                Object val = BeanUtil.getPropertyValue(comp, "model.minimum");
                if (val != null)
                {
                    cols = String.valueOf(val).length();
                }
                val = BeanUtil.getPropertyValue(comp, "model.maximum");
                if (val != null)
                {
                    cols = Math.max(cols, String.valueOf(val).length());
                }
                if (cols > 0)
                {
                    BeanUtil.setPropertyValue(context, comp, "editor.components[0].columns", new Integer(cols));
                }
                */
                ret = comp;
            }
            else if (type.equals("text"))
            {
                JTextField comp = new JTextField();
                comp.setText(PropertiesHelper.getString(config, "text", ""));
                comp.setBorder(null);
                comp.setBackground(null);
                comp.setEditable(false);
                String str = PropertiesHelper.getString(config, "align", null);
                if (str != null)
                {
                    comp.setHorizontalAlignment(PropertiesHelper.getInt(BeanUtil.getPropertyValue(comp, str.toUpperCase()), JTextField.LEFT));
                }
                ret = comp;
            }
            else if (type.equals("textArea"))
            {
                JTextArea comp = new JTextArea();
                JScrollPane pane = new JScrollPane(comp);
                retComp = comp;
                ret = pane;
            }
            else if (type.equals("textField"))
            {
                JTextField comp = new JTextField();
                ret = comp;
            }
            else if (type.equals("horizontalBox"))
            {
                Box box = Box.createHorizontalBox();
                String[] components = PropertiesHelper.getStringArray(config, "components");
                if (components != null)
                {
                    for (int i=0 ; i < components.length ; i++)
                    {
                        String cname = components[i];
                        Object obj = context.getElementManager().getApplicationElement(cname);
                        if (obj instanceof Component)
                        {
                            box.add((Component)obj);
                        }
                    }
                }
                ret = box;
            }
            else if (type.equals("tabbedPane"))
            {
                JTabbedPane pane = new JTabbedPane();
                String[] tabs = PropertiesHelper.getStringArray(config, "tabs");
                if (tabs != null)
                {
                    for (int i=0 ; i < tabs.length ; i++)
                    {
                        String tab = tabs[i];
                        Map tconfig = PropertiesHelper.getSubset(context.getConfiguration(), tab + ".");

                        Component comp = null;
                        String title = PropertiesHelper.getString(tconfig, "title", tab);
                        String compName = PropertiesHelper.getString(tconfig, "component");
                        if (compName != null)
                        {
                            Object obj = context.getElementManager().getApplicationElement(compName);
                            if (obj instanceof Component)
                            {
                                comp = (Component)obj;
                            }
                        }

                        if (comp == null)
                        {
                            comp = new JPanel();
                        }
                        pane.addTab(title, comp);

                        boolean selected = PropertiesHelper.getBoolean(tconfig, "selected", false);
                        if (selected)
                        {
                            pane.setSelectedComponent(comp);
                        }
                    }
                }
                ret = pane;
            }
            else if (type.length() > 2)
            {
                // try Swing component with no-arg constructor
                String cname = "javax.swing.J" + type.substring(0,1).toUpperCase() + type.substring(1, type.length());
                try
                {
                    ret = (Component)ReflectUtil.createInstance(cname);
                }
                catch (ClassNotFoundException e)
                {
                }
                catch (NoSuchMethodException e)
                {
                }
                catch (InstantiationException e)
                {
                }
                catch (IllegalAccessException e)
                {
                }
                catch (InvocationTargetException e)
                {
                }
            }
        }
        else if ((type = PropertiesHelper.getString(config, "typeClassName", null)) != null)
        {
            HashMap over = new HashMap();
            over.put("className", type);
            ret = (Component)ManagedElementFactory.getInstance(context).createManagedElement(elementId, null, over);
        }

        if (retComp != null)
        {
            configureComponent(context, elementId, retComp);
        }
        else if (ret != null)
        {
            configureComponent(context, elementId, ret);
        }

        return ret;
    }

    protected static void configureComponent(ApplicationContext context, String elementId, Component comp)
    {
        createEmitters(context, elementId, comp);

        Map config = PropertiesHelper.getSubset(context.getConfiguration(), elementId + ".");
        String popup = PropertiesHelper.getString(config, "popupElement", null);
        if (popup != null)
        {
            Object obj = context.getElementManager().getApplicationElement(popup);
            if (obj instanceof JPopupMenu)
            {
                new PopupApplicationContextAdapter(context, elementId, comp, (JPopupMenu)obj);
            }
        }

        if (comp instanceof JComponent)
        {
            Border border = createBorder(PropertiesHelper.getString(config, "border"));
            if (border != null)
            {
                ((JComponent)comp).setBorder(border);
            }
        }
    }

    protected static Border createBorder(String borderstr)
    {
        Border border = null;

        String[] borders = PropertiesHelper.getStringArray(borderstr, ";");
        if (borders != null)
        {
            for (int i=0 ; i < borders.length ; i++)
            {
                String bordstr = borders[i];
                String type = "empty";
                Object[] args = null;
                int idx = bordstr.indexOf(':');
                if (idx != -1)
                {
                    type = bordstr.substring(0, idx).toLowerCase();
                    if (bordstr.length() > idx+1)
                    {
                        bordstr = bordstr.substring(idx+1);
                    }
                    else
                    {
                        bordstr = null;
                    }
                }
                if (bordstr != null)
                {
                    String titled = "titled";
                    if (type.startsWith(titled))
                    {
                        String sep = ",";
                        if (type.length() > titled.length())
                        {
                            sep = type.substring(titled.length()) + sep;
                            type = titled;
                        }
                        int sidx = bordstr.indexOf(sep);
                        if (sidx != -1)
                        {
                            String title = bordstr.substring(0, sidx);
                            sidx += sep.length();
                            if (sidx < bordstr.length()-1)
                            {
                                String basebord = bordstr.substring(sidx);
                                args = new Object[]{createBorder(basebord), title};
                            }
                        }
                    }
                    else
                    {
                        String[] parts = PropertiesHelper.getStringArray(bordstr);
                        if (parts != null)
                        {
                            args = new Object[parts.length];
                            System.arraycopy(parts,0, args,0,parts.length);
                        }
                    }
                }

                if (type.length() > 0)
                {
                    if (type.length() > 1)
                    {
                        type = type.substring(0,1).toUpperCase() + type.substring(1, type.length());
                    }
                    else
                    {
                        type = type.toUpperCase();
                    }
                }
                
                String method = "create" + type + "Border";
                try
                {
                    Border bord = (Border)ReflectUtil.invokeMethod(BorderFactory.class, method, args);
                    if (border == null)
                    {
                        border = bord;
                    }
                    else
                    {
                        border = BorderFactory.createCompoundBorder(bord, border);
                    }
                }
                catch (NoSuchMethodException e)
                {
                }
                catch (IllegalAccessException e)
                {
                }
                catch (InvocationTargetException e)
                {
                }
            }
        }

        return border;
    }

    protected static void createEmitters(ApplicationContext context, String elementId, Component comp)
    {
        Map config = PropertiesHelper.getSubset(context.getConfiguration(), elementId + ".");

        // emitter
        String emit = PropertiesHelper.getString(config, "emitApplicationContext", null);
        if (emit != null)
        {
            if (comp instanceof JFormattedTextField)
            {
                if (emit.indexOf(VALUE) != -1)
                {
                    new PropertyChangeEmitter(context, getKey(emit, VALUE, elementId), comp);
                }
            }

            if (comp instanceof JTextComponent)
            {
                if (emit.indexOf(DOCUMENT) != -1)
                {
                    new DocumentEmitter(context, getKey(emit, DOCUMENT, elementId), ((JTextComponent)comp).getDocument());
                }
                if (emit.indexOf(ACTION) != -1)
                {
                    new ActionEmitter(context, getKey(emit, ACTION, elementId), comp);
                }
            }
            else if (emit.startsWith(SELECT))
            {
                new SelectionEmitter(context, getKey(emit, SELECT, elementId), getKey(emit, SELECT, elementId), comp);
            }
            else if (emit.startsWith(ITEM))
            {
                new ItemEmitter(context, getKey(emit, ITEM, elementId), comp);
            }
            else if (emit.startsWith(ACTION))
            {
                new ActionEmitter(context, getKey(emit, ACTION, elementId), comp);
            }

            if (emit.startsWith(HYPERLINK))
            {
                String prefix = HYPERLINK;
                String[] opts = {HYPERLINK + "activated", HYPERLINK + "entered", HYPERLINK + "exited"};
                for (int i=0 ; i < opts.length ; i++)
                {
                    if (emit.toLowerCase().startsWith(opts[i]))
                    {
                        prefix = opts[i];
                        break;
                    }
                }
                new HyperlinkEmitter(context, getKey(emit, prefix, elementId), prefix, comp);
            }
        }

        // mediator
        String mediate = PropertiesHelper.getString(config, "mediateApplicationContext", null);
        if (mediate != null)
        {
            if (mediate.startsWith(ACTION))
            {
                new ActionMediator(context, getKey(mediate, ACTION, elementId), comp);
            }
            else if (mediate.startsWith(CHANGE))
            {
                new ChangeMediator(context, elementId, getKey(mediate, CHANGE, elementId), comp);
            }
            else if (mediate.startsWith(DOCUMENT))
            {
                if (comp instanceof JTextComponent)
                {
                    JTextComponent tcomp = (JTextComponent)comp;
                    new DocumentMediator(context, getKey(mediate, DOCUMENT, elementId), tcomp.getDocument());
                }
            }
            else if (mediate.startsWith(ITEM))
            {
                new ItemMediator(context, getKey(mediate, ITEM, elementId), comp);
            }
            else if (mediate.startsWith(SELECT))
            {
                new SelectionMediator(context, elementId, getKey(mediate, SELECT, elementId), comp);
            }
            else if (mediate.startsWith(VALUE))
            {
                new PropertyChangeMediator(context, getKey(mediate, VALUE, elementId), comp);
            }
        }

        // receiver
        String receive = PropertiesHelper.getString(config, "receiveApplicationContext", null);
        if (receive != null)
        {
            String property = PropertiesHelper.getString(config, "receiveApplicationContext.property", "value");
            String valueProperty = PropertiesHelper.getString(config, "receiveApplicationContext.valueProperty", null);
            if (receive.startsWith(ACTION))
            {
                new DefaultReceiver(context, getKey(receive, ACTION, elementId) + ".actionPerformed", property, valueProperty, comp);
            }
            else if (receive.startsWith(CHANGE))
            {
                new DefaultReceiver(context, getKey(receive, CHANGE, elementId) + ".selectValue", property, valueProperty, comp);
            }
            else if (receive.startsWith(DOCUMENT))
            {
                new DefaultReceiver(context, getKey(receive, DOCUMENT, elementId) + ".selectValue", property, valueProperty, comp);
            }
            else if (receive.startsWith(VALUE))
            {
                new DefaultReceiver(context, getKey(receive, VALUE, elementId) + ".selectValue", property, valueProperty, comp);
            }
            else if (receive.startsWith(SELECT))
            {
                new DefaultReceiver(context, getKey(receive, SELECT, elementId) + ".selectValue", property, valueProperty, comp);
            }
        }
    }

    private static DefaultListModel createListModel(ApplicationContext context, Map config, String elementId)
    {
        DefaultListModel model = null;

        String[] items = PropertiesHelper.getStringArray(config, "items");
        if (items != null)
        {
            model = createListModel(items);
        }
        else if ((items = PropertiesHelper.getStringArray(config, "itemElements")) != null)
        {
            ArrayList arr = new ArrayList();
            for (int i=0 ; i < items.length ; i++)
            {
                Object obj = context.getElementManager().getApplicationElement(items[i]);
                if (obj != null)
                {
                    arr.add(obj);
                }
            }
            if (arr.size() > 0)
            {
                model = createListModel(arr);
            }
        }
        else
        {
            String item = PropertiesHelper.getString(config, "itemsElement", null);
            if (item != null)
            {
                Object obj = context.getElementManager().getApplicationElement(item);
                if (obj != null)
                {
                    model = createListModel(obj);
                }
            }
        }

        if (model == null)
        {
            model = new DefaultListModel();
        }

        return model;
    }

    private static DefaultListModel createListModel(Object obj)
    {
        DefaultListModel model = null;
        if (obj == null)
        {
            obj = new ArrayList();
        }
        if (obj instanceof Object[])
        {
            obj = Arrays.asList((Object[])obj);
        }
        else if (obj.getClass().isArray())
        {
            // primitive array
            ArrayList arr = new ArrayList();
            int size = Array.getLength(obj);
            for (int i=0 ; i < size ; i++)
            {
                arr.add(Array.get(obj, i));
            }
            obj = arr;
        }

        if (obj instanceof Collection)
        {
            model = new DefaultListModel();
            Collection col = (Collection)obj;
            Iterator iter = col.iterator();
            while (iter.hasNext())
            {
                model.addElement(iter.next());
            }
        }
        return model;
    }

    private static String getKey(String base, String prefix, String def)
    {
        String ret = def;
        if (base.length() > prefix.length())
        {
            ret = base.substring(prefix.length());
        }
        return ret;
    }

    private static Component getPanelComponent(ApplicationContext context, String elementId, Map panelConfig, GridBagConstraints gbc)
    {
        Component comp;
        String fill = "[fill";
        if (elementId.startsWith(fill) && elementId.endsWith("]"))
        {
            Dimension min = null;
            Dimension pref = null;
            Dimension max = null;
            
            int width = -1;
            int height = -1;
            if ((elementId.length() > fill.length()+3) && (elementId.charAt(fill.length()) == ':'))
            {
                String str = elementId.substring(fill.length()+1, elementId.length()-1);
                if (str.indexOf(' ') == -1)
                {
                    width = Integer.parseInt(str);
                }
                else
                {
                    String[] vals = PropertiesHelper.getStringArray(str, " ");
                    if (vals[0].length() > 0)
                    {
                        width = Integer.parseInt(vals[0]);
                    }
                    if (vals[1].length() > 0)
                    {
                        height = Integer.parseInt(vals[1]);
                    }
                }
            }

            boolean fixWidth = (width >= 0);
            boolean fixHeight = (height >= 0);
            if (fixWidth)
            {
                // fixed width
                min = new Dimension(width, fixHeight ? height : 0); 
                pref = new Dimension(width, fixHeight ? height : 0); 
                max = new Dimension(width, fixHeight ? height : Integer.MAX_VALUE); 
            }
            else if (fixHeight)
            {
                // fixed height
                min = new Dimension(0, height); 
                pref = new Dimension(0, height); 
                max = new Dimension(Integer.MAX_VALUE, height); 
            }

            comp = new Box.Filler(min, pref, max);
            if (gbc != null)
            {
                if (!fixWidth)
                {
                    gbc.fill = fixHeight ? GridBagConstraints.HORIZONTAL : GridBagConstraints.BOTH;
                }
                else if (!fixHeight)
                {
                    gbc.fill = GridBagConstraints.VERTICAL;
                }
            }
        }
        else if (elementId.startsWith("[") && elementId.endsWith("]") && (elementId.length() > 2))
        {
            String id = elementId.substring(1, elementId.length() - 1);
            String type = null;
            int idx = elementId.indexOf(':');
            if (idx > 0)
            {
                id = elementId.substring(1,idx);
                type = elementId.substring(idx+1, elementId.length()-1);
                if (type.length() == 0)
                {
                    type = null;
                }
            }
            comp = (Component)createInstance(new ApplicationElementContext(context, id), type);
            if (comp != null)
            {
                ManagedElementFactory.getInstance(context).configureElementProperties(context, id, comp);
            }
            else
            {
                if (type == null)
                {
                    comp = getPanelComponent(context, id, new HashMap(), null);
                }
            }
        }
        else    if (PropertiesHelper.getString(context.getConfiguration(), elementId + ".className", null) != null)
        {
            comp = (Component)context.getElementManager().getApplicationElement(elementId);
        }
        else
        {
            // assume this is an action
            Map overrides = PropertiesHelper.getSubset(panelConfig, elementId + ".");
            comp = ActionGUIFactory.getInstance((SwingApplicationContext)context).createButton(elementId, overrides);
        }
        return comp;
    }
}



