com.arc.seecode.internal.display.SeeCodeTextViewerFactory.java Source code

Java tutorial

Introduction

Here is the source code for com.arc.seecode.internal.display.SeeCodeTextViewerFactory.java

Source

/*******************************************************************************
 * Copyright (c) 2005-2012 Synopsys, Incorporated
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * Synopsys, Inc - Initial implementation 
 *******************************************************************************/
package com.arc.seecode.internal.display;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.WeakHashMap;

import org.dom4j.Element;

import com.arc.mw.util.Cast;
import com.arc.mw.util.StringUtil;
import com.arc.seecode.display.IContext;
import com.arc.seecode.display.ISeeCodeTextViewer;
import com.arc.seecode.display.ISeeCodeTextViewerCallback;
import com.arc.seecode.display.ISeeCodeTextViewerFactory;
import com.arc.seecode.display.IToolBarBuilderFactory;
import com.arc.seecode.display.MenuDescriptor;
import com.arc.seecode.engine.display.IDisplayCreator;
import com.arc.seecode.internal.display.panels.ExtensionsPanel;
import com.arc.widgets.IColor;
import com.arc.widgets.IComponentFactory;
import com.arc.widgets.IContainer;
import com.arc.widgets.IImageWidget;
import com.arc.widgets.IToolBarBuilder;
import com.arc.widgets.IToolItem;

/**
 * @author David Pickens
 */
public class SeeCodeTextViewerFactory implements ISeeCodeTextViewerFactory {
    private static final String PANELS_PACKAGE = "com.arc.seecode.internal.display.panels";

    private List<ISeeCodeTextViewer> mDisplayList = new ArrayList<ISeeCodeTextViewer>();

    private IComponentFactory mWidgetFactory;
    private Map<IImageWidget, SeeCodeImage> mImageWidgetWrapper = null;

    private IToolBarBuilderFactory mToolBarBuilderFactory;

    private IContext fContext;

    private String fDebuggerInstallPath = null; //e.g. "C:/arc/metaware/arc"
    private IDisplayCreator fDisplayCreator;

    /**
     * 
     * @param factory the widget factory for creating widgets (Swing or SWT).
     * @param tbBuilderFactory a toolbar builder factory
     * @param context a callback to get thread information if a display needs
     * to provide, say, a thread selection combobox.
     * @param scDir the debugger installation directory (e.g., "C:/arc/metaware/arc").
     * @param displayCreator a callback for creating a display.
     */
    public SeeCodeTextViewerFactory(IComponentFactory factory, IToolBarBuilderFactory tbBuilderFactory,
            IContext context, String scDir, IDisplayCreator displayCreator) {
        if (tbBuilderFactory == null || factory == null) {
            throw new IllegalArgumentException("factory is null");
        }
        mWidgetFactory = factory;
        mToolBarBuilderFactory = tbBuilderFactory;
        fContext = context;
        fDebuggerInstallPath = scDir;
        fDisplayCreator = displayCreator;
    }

    private static String capitalize(String s) {
        if (s == null)
            return null;
        if (s.length() == 0)
            return s;
        if (s.indexOf('_') >= 0) {
            //Get rid of unconventional underscores
            // E.g., "mem_no_examine" ==> "MemNoExamine"
            StringBuffer buf = new StringBuffer(s.length());
            int i = s.indexOf('_');
            while (i >= 0) {
                if (i > 0)
                    buf.append(s.substring(0, 1).toUpperCase() + s.substring(1, i));
                s = s.substring(i + 1);
                i = s.indexOf('_');
            }
            if (s.length() > 0) {
                buf.append(s.substring(0, 1).toUpperCase() + s.substring(1));
            }
            return buf.toString();
        } else
            return s.substring(0, 1).toUpperCase() + s.substring(1);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.arc.seecode.display.ISeeCodeTextViewerFactory#createDisplay(java.util.Properties,
     *      java.lang.Object, java.lang.Object,
     *      com.arc.seecode.display.ISeeCodeTextViewerCallback)
     */
    @Override
    public ISeeCodeTextViewer createDisplay(int id, Properties properties, IContainer parent,
            ISeeCodeTextViewerCallback callback) {
        ISeeCodeTextViewer existing = lookupDisplay(id);
        if (existing != null && !(existing instanceof DummyViewer)) {
            callback.internalError(lookupDisplay(id), "Window ID is not unique! (=" + id + ")", null);
            return existing;
        }
        // create command
        // The UI has asked us to create a display.
        // Let's ensure that what follows are properties.
        // I.e.:
        //  hide=0/1 (or missing: implies 0)
        //  guic=xxx (specify gui components to add)
        //  lines=NNN (lines in the display)
        //  kind=keyword (kind of display)
        //  UM_window=1 or missing. If 1, says window is user-managed
        //  connectable=1 or missing. If 1, says window
        //          needs to have the connect field for cmpd.
        // Add a dummy element, so windows start at 1.
        // 0 -> command window
        // 1 -> mdi frame
        // The GUI returns the number of this display, which the engine
        // tucks away and uses to talk back to Java.
        // The future window id, after we add it to
        // the set of windows.

        boolean UM_window = properties.getProperty("UM_window", "0").charAt(0) == '1';
        if (UM_window) {
            callback.internalError(null, "UM_windows no longer supported", null);
            // return create_UM_window(properties, window_id,toEngine);
        }
        //        boolean initially_hide =
        //            properties.getProperty(Globals.hide_string, "0").charAt(0) == '1';
        final String kind_of_display = properties.getProperty("kind", "unknown");
        String gui_components = properties.getProperty("guic", "");
        String extra_class_name = properties.getProperty("class");

        //
        //"clone_from=<wid>" means that the display that we're about to
        // create is actually a clone of window id "wid" in the parent
        // desktop frame.
        //        String clone_from_string = properties.getProperty("cloned_from");
        //        int cloneFrom = 0;
        //        if (clone_from_string != null) {
        //            cloneFrom = Integer.parseInt(clone_from_string);
        //        }
        SeeCodeTextViewer display = new SeeCodeTextViewer(id, kind_of_display, parent, callback, mWidgetFactory,
                this);
        mDisplayList.add(display);
        ValueSender sender = new ValueSender(display, callback);
        // persistent=0 means that we don't save the window layout
        String persistentString = properties.getProperty("persistent");
        boolean persistent = persistentString == null || !persistentString.equals("0");
        ExtensionsPanel panel = null;
        // Now try to find the XXX_extra panel to add to this display.
        try {
            String S = extra_class_name != null ? extra_class_name : kind_of_display;
            Class<?> extra_panel_class = Class.forName(PANELS_PACKAGE + "." + capitalize(S) + "Panel");
            panel = (ExtensionsPanel) extra_panel_class.newInstance();
        } catch (Exception e) {
            panel = new ExtensionsPanel();
        }
        IToolBarBuilder toolBarBuilder = mToolBarBuilderFactory.createToolBarBuilder(display.getComponent());
        panel.init(display, sender, toolBarBuilder, mWidgetFactory, fContext);
        display.setPersistent(persistent);
        //display.setName(makeWindowName(kind_of_display));

        if (gui_components.length() > 0) {
            addWidgetsToExtensionsPanel(panel, gui_components, display, sender, callback,
                    new CheckBoxRecorder(display));
        }
        //Now finish constructing the display since the
        // extensions panel is built.
        display.init(properties, panel, mWidgetFactory, toolBarBuilder);

        panel.finish();
        return display;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.arc.seecode.display.ISeeCodeTextViewerFactory#getDisplay(int)
     */
    @Override
    public ISeeCodeTextViewer getDisplay(int idNumber) throws IllegalArgumentException {
        ISeeCodeTextViewer v = lookupDisplay(idNumber);
        if (v == null) {
            mDisplayList.add(new DummyViewer(idNumber)); // prevent duplicate errors.
            throw new IllegalArgumentException("Invalid window ID: " + idNumber);
        }
        return v;
    }

    /**
     * Return the {@link SeeCodeImage} object that wraps
     * a particular {@link IImageWidget}.
     * @param w the ImageWidget that we're looking for the
     * wrapper for.
     * @return the wrapper for the image widget.
     */
    SeeCodeImage getSeeCodeImageFor(IImageWidget w) {
        if (mImageWidgetWrapper == null)
            return null;
        return mImageWidgetWrapper.get(w);
    }

    /**
     * @param idNumber
     * @return the display corresponding to the id number.
     */
    private ISeeCodeTextViewer lookupDisplay(int idNumber) {
        for (ISeeCodeTextViewer v : mDisplayList) {
            if (v.getDisplayID() == idNumber)
                return v;
        }
        return null;
    }

    void remove(ISeeCodeTextViewer viewer) {
        mDisplayList.remove(viewer);
    }

    /**
     * Called to add a dummy viewer when we're forceably
     * closing one that the engine is possibly updating.
     * @param viewer
     */
    void add(ISeeCodeTextViewer viewer) {
        ISeeCodeTextViewer existing = lookupDisplay(viewer.getDisplayID());
        if (existing != null) {
            throw new IllegalArgumentException("viewer in use: " + viewer.getDisplayID());
        }
        mDisplayList.add(viewer);
    }

    @Override
    public void addUserGUI(int ugui_number, String specification, MenuDescriptor menu,
            ISeeCodeTextViewerCallback callback, final ISeeCodeTextViewer viewer) {
        ValueSender sender = new ValueSender(viewer, callback);
        addUserGui(ugui_number, specification, menu, callback, viewer, sender);
    }

    /**
     * @param ugui_number
     * @param specification
     * @param menu
     * @param callback
     * @param viewer
     * @param sender
     */
    void addUserGui(int ugui_number, String specification, MenuDescriptor menu, ISeeCodeTextViewerCallback callback,
            final ISeeCodeTextViewer viewer, IValueSender sender) {
        ICheckBoxRecorder cbrecorder = new CheckBoxRecorder(viewer);
        addUserGUI(ugui_number, specification, menu, callback, sender, cbrecorder);
    }

    private void addUserGUI(final int ugui_number, String specification, MenuDescriptor menu,
            final ISeeCodeTextViewerCallback callback, IValueSender sender, ICheckBoxRecorder cbrecorder) {
        // Add user GUI. This is expressed via the XML-like language
        // used by the options processor.
        // Globals.SOP("Processing user GUI specification "+specification);
        Element root = null;
        try {
            root = com.metaware.guihili.GuihiliParser.parseString(specification).getRootElement();
            List<Element> elements = Cast.toType(root.elements());
            if (elements.size() == 1)
                root = elements.get(0);
        } catch (Exception x) {
            callback.internalError(null, "Guihili processing error for " + specification, x);
            return;
        }
        if (root == null) {
            callback.notifyError(null, "Invalid GUI specification: " + specification, "Configuration error");
            return;
        }
        new UserGuiWalker(sender, ugui_number, cbrecorder, fDisplayCreator).walk(root, menu);
    }

    IComponentFactory getWidgetFactory() {
        return mWidgetFactory;
    }

    //    private String makeWindowName(String kind_of_display) {
    //        // Assign this a name so we can use it symbolically
    //        // for recordings.
    //        char suffix = '0';
    //        boolean done;
    //        String wname;
    //        do {
    //            done = true;
    //            wname = (kind_of_display + suffix).intern();
    //            for (int i = 0; i < mDisplayList.size(); i++) {
    //                if (((SeeCodeTextViewer) mDisplayList.get(i)).getName() == wname) {
    //                    // Duplication.
    //                    done = false;
    //                    break;
    //                }
    //            }
    //            if (!done) {
    //                suffix = (char) (suffix + 1);
    //            }
    //        } while (!done);
    //        // Now add the wname.
    //        return wname;
    //    }

    // Inherited from Tom
    private void addWidgetsToExtensionsPanel(ExtensionsPanel panel, String s, SeeCodeTextViewer dtd,
            IValueSender sender, ISeeCodeTextViewerCallback callback, ICheckBoxRecorder cbRecorder) {
        if (s.indexOf("=") > 0) {
            // New format.
            List<String> things = extract_properties_from_property_sequence(s);
            for (String thing : things) {
                makeWidgetFromPropertyString(panel, thing, sender, callback, cbRecorder);
            }
        } else {
            // Old format, that is still used.
            String options[] = StringUtil.stringToArray(s);
            for (int i = 0; i < options.length; i++) {
                makeOldStyleGuiComponent(options[i], panel, sender);
            }
        }
    }

    //Inherited from Tom
    private void makeOldStyleGuiComponent(String s, ExtensionsPanel panel, final IValueSender sender) {
        StringTokenizer ST = new StringTokenizer(s, ":");
        String A = ST.nextToken();
        if (A.equals("choice")) {
            // Format of such direction:
            // choice:Tip:A,B,C:index:value_name:docname
            //         ^ tip to be displayed
            //          ^^^ the choices
            //               ^^ the index of the default choice
            //                ^^^ the value name to be sent to engine
            //                    when a value changes.
            //                         ^^^ doc file name.
            // Get a choice box.
            String tip = ST.nextToken();
            String choice_spec = ST.nextToken();
            String Default = ST.nextToken();
            String value_id = ST.nextToken();
            //String docname = ST.hasMoreTokens()?ST.nextToken():null;
            panel.makeChoice(StringUtil.pathToArray(choice_spec, ","), Default, value_id, tip, true, sender);
        } else if (A.equals("text")) {
            String Label = ST.nextToken();
            String tip = ST.nextToken();
            String size = ST.nextToken();
            String value_id = ST.nextToken();
            //String docname = ST.hasMoreTokens()?ST.nextToken():null;
            panel.makeTextField(Label, value_id, tip, Integer.parseInt(size), true, true, sender);
        } else if (A.equals("button")) {
            // button:name:tip:value_name
            String Label = ST.nextToken();
            String tip = ST.nextToken();
            String value_id = ST.nextToken();
            //String docname = ST.hasMoreTokens()?ST.nextToken():null;
            panel.makeButton(Label, value_id, tip);
        } else if (A.equals("boolean_button")) {
            // bool_button:name0:tio0:name1:tip1:default#:id
            String name0 = ST.nextToken();
            String tip0 = ST.nextToken();
            String name1 = ST.nextToken();
            String tip1 = ST.nextToken();
            // Default value must be 0 or 1.
            String default_value = ST.nextToken();
            String value_id = ST.nextToken();
            //String docname = ST.hasMoreTokens()?ST.nextToken():null;
            panel.makeBooleanToggle(value_id, "1".equals(default_value), name0, name1, tip0, tip1, true);
        }
    }

    /**
     * Append a widget to "extensions panel" of display as described in property
     * string.
     * 
     * @param component_prop
     *            string representation of property values.
     */
    void makeWidgetFromPropertyString(ExtensionsPanel panel, String component_prop, IValueSender sender,
            ISeeCodeTextViewerCallback callback, ICheckBoxRecorder cbRecorder) {
        Properties props = loadProperties(component_prop);
        String sep = props.getProperty("separator");
        boolean needsSeparator = sep != null && sep.startsWith("1");
        if (needsSeparator) {
            panel.makeSeparator();
        }

        // Globals.SOP("GUI properties:"+P.toString());
        String kind = props.getProperty("kind");
        String tip = props.getProperty("tip");
        String value_id = props.getProperty("id");
        //        String docname = props.getProperty("docname");
        //        String doc_contents = props.getProperty("doccontents");
        String save_component_state_string = props.getProperty("save_component_state");
        // We by default save state unless otherwise told.
        boolean saveComponentState = save_component_state_string == null
                || save_component_state_string.charAt(0) == '1';
        if (kind.equals("choice")) {
            String choiceString = props.getProperty("choices", "");
            String init_choice = props.getProperty("init_choice");
            String[] itemList = StringUtil.pathToArray(choiceString, ",");
            panel.makeChoice(itemList, init_choice, value_id, tip, saveComponentState, sender);
        } else if (kind.equals("text")) {
            String label = props.getProperty("label");
            if (label.equals("save_to_file")) {
                panel.setSaveToFileAction(value_id);
            } else {
                String size = props.getProperty("min_size");
                String CR = props.getProperty("CR_needed");
                //String shift_left_string = props.getProperty("shift_left");
                //boolean shiftLeft = shift_left_string == null
                //        || shift_left_string.charAt(0) == '1';
                boolean CR_needed = CR == null || CR.charAt(0) != '0';
                panel.makeTextField(label, value_id, tip, Integer.parseInt(size), saveComponentState, CR_needed,
                        sender);
            }
        } else if (kind.equals("label")) {
            String label = props.getProperty("label", "??label??");
            panel.makeLabel(label, null);
        } else if (kind.equals("button")) {
            String label = props.getProperty("label");
            if (label.equals("double_click")) {
                panel.setDoubleClickAction(value_id);
            } else if (label.equals("column0_click")) {
                panel.setColumn0ClickAction(value_id);
            } else if (label.equals("want_tooltips")) {
                panel.setWantsToolTipAction(value_id);
            } else {
                //                String repeatable = props.getProperty("repeatable");
                //            int repeatMode = repeatable != null ?
                // Integer.parseInt(repeatable)
                //                    : 0;
                //            int repeatDelay = 0;
                //            switch (repeatMode) {
                //                case 1:
                //                    repeatDelay = RepeatingButton.SLOW;
                //                    break;
                //                case 2:
                //                    repeatDelay = RepeatingButton.MEDIUM;
                //                    break;
                //                case 3:
                //                    repeatDelay = RepeatingButton.FAST;
                //                    break;
                //            }
                String enabled = props.getProperty("enabled");
                int E = enabled != null ? Integer.parseInt(enabled) : 1;
                IToolItem b = panel.makeButton(label, value_id, tip);
                if (E == 0) {
                    b.setEnabled(false);
                }
            }
        } else if (kind.equals("LED")) {
            // led: color=string, tip=string
            String color = props.getProperty("color");
            //String orientation = props.getProperty("orientation");

            IColor ledColor;
            try {
                ledColor = mWidgetFactory.makeColor(color);
            } catch (IllegalArgumentException x) {
                ledColor = mWidgetFactory.makeColor("black");
            }
            IImageWidget widget = panel.makeLED(ledColor, value_id);
            widget.setToolTipText(tip);
        } else if (kind.equals("image")) {
            String W = props.getProperty("width");
            String H = props.getProperty("height");
            String Label = props.getProperty("label");
            String _32_bit = props.getProperty("32");
            SeeCodeImage si = new SeeCodeImage(panel.getControl(), Integer.parseInt(W), Integer.parseInt(H),
                    _32_bit != null ? Integer.parseInt(_32_bit) != 0 : false, Label, tip, value_id,
                    this.fDebuggerInstallPath, getWidgetFactory());
            panel.registerToolBarItem(si.getImageWidget(), value_id);
            if (mImageWidgetWrapper == null) {
                mImageWidgetWrapper = new WeakHashMap<IImageWidget, SeeCodeImage>();
            }
            mImageWidgetWrapper.put(si.getImageWidget(), si);
        } else if (kind.equals("popup_button")) {
            String label = props.getProperty("label");
            makeMenuItem(panel.getStaticMenuDescriptor(), label, value_id, sender);
        } else if (kind.equals("boolean_button")) {
            // bool_button:name0:tio0:name1:tip1:default#:id
            String label0 = props.getProperty("label0");
            String label1 = props.getProperty("label1");
            String tip0 = props.getProperty("tip0");
            String tip1 = props.getProperty("tip1");
            // Default value must be 0 or 1.
            String default_value = props.getProperty("init_state");
            panel.makeBooleanToggle(value_id, "1".equals(default_value), label0, label1, tip0, tip1,
                    saveComponentState);
        } else if (kind.equals("next_line")) {
            // Dummy component that specifies that the GUI components
            // should "shift" to the next line.
            panel.newLine();
        } else if (kind.equals("user_gui")) {
            String ugui = props.getProperty("user_gui");
            addUserGUI(0, ugui, panel.getStaticMenuDescriptor(), callback, sender, cbRecorder);
        } else {
            callback.internalError(null, "Don't recognize GUI component: " + kind, null);
        }
    }

    private void makeMenuItem(MenuDescriptor menu, String label, final String value_id, final IValueSender sender) {
        menu.addMenuItem(value_id, label, new MenuDescriptor.IActionObserver() {

            @Override
            public void actionPerformed(String name) {
                sender.sendValueUpdate(value_id, "1");
            }
        });
    }

    //    private IButton makeBooleanToggle(IContainer parent, final String valueID,
    //            int initValue, final String label1, final String label2,
    //            final String tip1, final String tip2, final IValueSender sender) {
    //        final IButton b = mWidgetFactory.makeToggleButton(parent);
    //        b.setName(valueID);
    //        b.setSelected(initValue != 0);
    //        setBooleanToggleAttributes(b, label1, label2, tip1, tip2);
    //
    //        b.addActionListener(new ActionListener() {
    //
    //            public void actionPerformed(ActionEvent event) {
    //                sender.sendValueUpdate(valueID, b.isSelected() ? "1" : "0");
    //                setBooleanToggleAttributes(b, label1, label2, tip1, tip2);
    //            }
    //        });
    //        return b;
    //    }

    //    private static void setBooleanToggleAttributes(IButton toggle,
    //            String label1, String label2, String tip1, String tip2) {
    //        if (!toggle.isSelected()) {
    //            toggle.setText(label1);
    //            toggle.setToolTipText(tip1);
    //        } else {
    //            toggle.setText(label2);
    //            toggle.setToolTipText(tip2);
    //        }
    //    }

    /**
     * Extract the lists and place them in sequence in a vector.
     * 
     * @param sequence
     *            a nested property list of the form 0=propery list 1=property
     *            list 2=property list
     */
    static List<String> extract_properties_from_property_sequence(String sequence) {
        Properties list = loadProperties(sequence);
        int max_hash = 1024;
        String HC[] = new String[max_hash];
        for (Object keyObject : list.keySet()) {
            String key = (String) keyObject;
            if (key.equals("")) {
                // java 1.2 somehow changed the way properties are
                // extracted, and goofed things up. So we have to
                // avoid the empty string as a key.
                continue;
            }
            // Globals.SOP("key is '"+key+"' value is "+list.get(key));
            int hc;
            switch (key.length()) {
            case 1:
                hc = key.charAt(0) - '0';
                break;
            case 2:
                hc = ((key.charAt(0) - '0') << 8) + (key.charAt(1) - '0');
                break;
            default:
                hc = key.hashCode();
                hc = hc % max_hash;
                break;
            }
            HC[hc] = (String) list.get(key);
            // Get the entries in key order. That's how we make
            // an ordered list. The engine uses single-character
            // keys to ensure hash ordering.
        }
        List<String> v = new ArrayList<String>();
        for (int i = 0; i < max_hash; i++) {
            if (HC[i] != null) {
                v.add(HC[i]);
            }
        }
        return v;
    }

    /**
     * Create <code>Properties</code> map from string representation.
     * 
     * @param props
     *            the string representation.
     * @return the resulting properties.
     */
    static Properties loadProperties(String props) {
        ByteArrayInputStream s = new ByteArrayInputStream(props.getBytes());
        Properties P = new Properties();
        try {
            P.load(s);
        } catch (Exception e) {
        }
        return P;
    }

    @Override
    public String getDebuggerInstallPath() {
        return this.fDebuggerInstallPath;
    }
}