com.arc.cdt.debug.seecode.internal.ui.GuihiliPage.java Source code

Java tutorial

Introduction

Here is the source code for com.arc.cdt.debug.seecode.internal.ui.GuihiliPage.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.cdt.debug.seecode.internal.ui;

import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.Action;

import org.dom4j.DocumentException;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.settings.model.CSourceEntry;
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.core.settings.model.ICProjectDescription;
import org.eclipse.cdt.core.settings.model.ICProjectDescriptionManager;
import org.eclipse.cdt.core.settings.model.ICSourceEntry;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo;
import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
import org.eclipse.cdt.managedbuilder.core.ManagedCProjectNature;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectNature;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.HelpEvent;
import org.eclipse.swt.events.HelpListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.PlatformUI;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import com.arc.cdt.debug.seecode.core.SeeCodePlugin;
import com.arc.cdt.debug.seecode.options.ConfigurationException;
import com.arc.cdt.debug.seecode.options.ISeeCodeOptionsAugmenter;
import com.arc.cdt.debug.seecode.options.SeeCodeOptions;
import com.arc.cdt.debug.seecode.ui.UISeeCodePlugin;
import com.arc.cdt.debug.seecode.ui.Utilities;
import com.arc.cdt.debug.seecode.ui.views.IContextHelpIds;
import com.arc.debugger.EngineLocator;
import com.arc.mw.util.ListAnalyzer;
import com.arc.mw.util.StringUtil;
import com.arc.seecode.engine.config.OptFileResolver;
import com.arc.widgets.IComponent;
import com.arc.widgets.IComponentFactory;
import com.arc.widgets.IContainer;
import com.metaware.guihili.EnvironmentFactory;
import com.metaware.guihili.Gui;
import com.metaware.guihili.IEnvironment;
import com.metaware.guihili.IExceptionHandler;
import com.metaware.guihili.IHelpHandler;
import com.metaware.guihili.PropertyStorage;

/**
 * A class for creating the Guihili options page.
 * @author davidp
 * @currentOwner <a href="mailto:davidp@arc.com">davidp</a>
 * @version $Revision$
 * @lastModified $Date$
 * @lastModifiedBy $Author$
 * @reviewed 0 $Revision:1$
 */
public class GuihiliPage {

    private static final int MIN_PREFERRED_WIDTH = 800;

    private static final int MIN_PREFERRED_HEIGHT = 600; // ARCompact requires this

    // private Composite mPanel;

    // private static final String TARGET = "target"; // key in attribute map

    private Gui fGUI;

    // private static final String GUIHILI_PROPERTIES =
    // "seecode.config.properties";

    private transient boolean mPendingPropertyChange = false;

    private Composite mParentControl;

    private boolean mCreated = false;

    private StyledText mOptions; // where seecode options placed.

    private List<String> mLastSeeCodeOptions = null;

    private Color mBlue = null;

    private transient boolean mPropertyChangeSuppressed = false;

    private long mTimeOfLastOptionUpdate = 0;

    private boolean mInvalidPagePending = false;

    private boolean mDirty = false;

    private String fPermanentErrorMessage = null;

    // private static final int DEFAULT_OPTIONS_HEIGHT = 40;

    private ISeeCodeOptionsAugmenter mSeeCodeOptionsAugmenter = null;

    private Runnable fInvokeWhenFirstDirtied;

    private IErrorSetter fErrorSetter;

    private Collection<String> mOriginalPropertyNames = null;

    public interface IErrorSetter {

        public void setErrorMessage(String msg);
    }

    /**
     * Create instance on behalf of a CMPD process number.
     * @param invokeWhenFirstDirtied called when properties are first dirted (to enable OK buttons, etc.)
     * @param errorSetter callback for setting error messages.
     */
    public GuihiliPage(Runnable invokeWhenFirstDirtied, IErrorSetter errorSetter) {
        this.fInvokeWhenFirstDirtied = invokeWhenFirstDirtied;
        this.fErrorSetter = errorSetter;
    }

    class OptionFieldUpdater extends Thread {

        private boolean _terminated = false;

        private boolean _updatePending = false;

        private static final int UPDATE_WAIT_TIME = 500;

        private Runnable _updateRunner;

        private Display _display;

        OptionFieldUpdater() {
            super("OptionFieldUpdater");
            _display = mParentControl.getShell().getDisplay();
            _updateRunner = new Runnable() {

                @Override
                public void run() {
                    updateSeeCodeOptions();
                }
            };

        }

        @Override
        public void run() {
            while (!_terminated) {
                synchronized (this) {
                    while (!_updatePending && !_terminated) {
                        try {
                            wait();
                        } catch (InterruptedException e) {
                        }
                    }
                }
                if (!_terminated) {
                    try {
                        sleep(UPDATE_WAIT_TIME);
                        synchronized (this) {
                            _updatePending = false;
                        }
                        _display.asyncExec(_updateRunner);
                    } catch (InterruptedException e) {
                    }
                }

            }
        }

        public void terminate() {
            _terminated = true;
            interrupt();
        }

        public synchronized void update() {
            if (!_updatePending) {
                _updatePending = true;
                notifyAll();
            }
        }
    }

    public Control getControl() {
        return mParentControl;
    }

    private OptionFieldUpdater mOptionFieldUpdater;

    private String fTarget;

    private int fProcessCount = 0;

    /**
     * Here we create the Guihili-based panel.
     */
    public void createControl(Composite parent) {
        // Foreground for SeeCode options
        mBlue = new Color(parent.getShell().getDisplay(), 0, 0, 255);
        // Guihili manager may have been created already
        // if "setDefaults" or "initializeFrom" called
        // first.
        // CORRECTION: we don't want to create the
        // control until we know the target platform.
        // if (mGui == null) createGui(null);
        // Create a dummy component that we will fill
        // in lazily when we know what the target is.

        mParentControl = new Composite(parent, 0);
        PlatformUI.getWorkbench().getHelpSystem().setHelp(mParentControl, IContextHelpIds.PREFIX + "debug_options");
        GridData gd = new GridData();
        gd.grabExcessHorizontalSpace = true;
        gd.grabExcessVerticalSpace = true;
        gd.horizontalAlignment = GridData.FILL;
        gd.verticalAlignment = GridData.FILL;
        mParentControl.setLayoutData(gd);
        GridLayout layout = new GridLayout();
        layout.numColumns = 1;
        mParentControl.setLayout(layout);

        mParentControl.addDisposeListener(new DisposeListener() {

            @Override
            public void widgetDisposed(DisposeEvent e) {
                if (fGUI != null) {
                    // disposes of images and such
                    fGUI.dispose();
                    fGUI = null;
                }
                if (mBlue != null)
                    mBlue.dispose();
                if (mOptionFieldUpdater != null)
                    mOptionFieldUpdater.terminate();
            }
        });

        if (fGUI != null) {
            createControlActual();
        }
    }

    protected void setDirty(boolean v) {
        mDirty = v;
    }

    public boolean isDirty() {
        return mDirty;
    }

    /**
     * Display page that indicates a missing, or unrecognizable, executable.
     * @param parent
     */
    private void displayInvalidPage(Composite parent) {
        if (mInvalidPagePending)
            return;
        mInvalidPagePending = true;
        Text text = new Text(parent, SWT.MULTI | SWT.READ_ONLY);
        GridData gd = new GridData();
        gd.grabExcessHorizontalSpace = true;
        gd.grabExcessVerticalSpace = true;
        gd.horizontalAlignment = GridData.FILL;
        gd.verticalAlignment = GridData.FILL;
        text.setLayoutData(gd);
        text.setText(
                "\n\n\nThe associated program is not an\nELF executable or has an\nunrecognized target processor type.");
        final Font font = new Font(parent.getDisplay(), text.getFont().getFontData()[0].getName(), 16, SWT.ITALIC);
        text.setFont(font);
        text.addDisposeListener(new DisposeListener() {

            @Override
            public void widgetDisposed(DisposeEvent e) {
                if (!font.isDisposed())
                    font.dispose();
            }
        });
        parent.layout(true);
    }

    private void createControlActual() {
        mCreated = false; // for benefit of property change listener
        fGUI.setFrame(mParentControl.getShell());
        IComponentFactory factory = UISeeCodePlugin.getWidgetFactory();
        fGUI.setComponentFactory(factory);
        // ScrolledComposite scroller = new ScrolledComposite(mParentControl,SWT.V_SCROLL|SWT.H_SCROLL);
        // scroller.setExpandVertical(true);
        // scroller.setExpandHorizontal(true);
        Composite scroller = mParentControl;
        // We can't compute the Guihili panel until the
        // target machine is known.
        IContainer pane = factory.wrapContainer(scroller, IComponentFactory.NO_STYLE);
        MyLayout layout = new MyLayout();
        mParentControl.setLayout(layout);
        fGUI.setParent(pane);

        try {
            // Toggle.set("TRACE_GUI", true);
            fGUI.readXML("features");
        } catch (SAXParseException x) {
            String msg = "At " + x.getSystemId() + ", line " + x.getLineNumber();
            Exception e = x.getException();
            if (e != null)
                msg += ": " + e.getMessage();
            else
                msg += ": " + x.getMessage();
            setErrorMessage("Internal error:\n" + msg);
            String scdir = (String) fGUI.getEnvironment().getSymbolValue("SCDIR");
            if (scdir == null)
                scdir = "";
            else
                scdir = "(at " + scdir + ")";
            UISeeCodePlugin.showError("(Internal Error) Debugger Options File Parse Error",
                    "A syntax error was detected in one of the MetaWare Debugger's Options\n" + "Description files "
                            + scdir + "\n\n" + msg);
        } catch (SAXException x) {
            Throwable e = x.getException();
            String msg;
            if (e != null) {
                if (e instanceof DocumentException) {
                    DocumentException dx = (DocumentException) e;
                    if (dx.getNestedException() != null) {
                        e = dx.getNestedException();
                    }
                }
                msg = e.getMessage();
                if (msg == null || msg.trim().length() == 0) {
                    msg = e.toString();
                }
            } else
                msg = x.toString();
            setErrorMessage("Form description error\n(" + msg + ")");
            String scdir = (String) fGUI.getEnvironment().getSymbolValue("SCDIR");
            if (scdir == null)
                scdir = "";
            else
                scdir = "(at " + scdir + ")";
            UISeeCodePlugin.showError("Debugger Installation Error",
                    "The MetaWare debugger " + scdir
                            + "\nmay not be installed correctly or there is an internal error\n"
                            + "with one of its Debugger Options description files:\n\n" + msg);
        } catch (FileNotFoundException x) {
            if (x.getMessage() != null && x.getMessage().indexOf("feature") >= 0) {
                setErrorMessage("Can't find the debugger on the search path");
                UISeeCodePlugin.showError("Installation Error",
                        "Cannot find the MetaWare debugger on the search path");
            } else {
                setErrorMessage("The debugger seems to be missing option description files: " + x);
                String scdir = (String) fGUI.getEnvironment().getSymbolValue("SCDIR");
                if (scdir == null)
                    scdir = "";
                else
                    scdir = "(at " + scdir + ")";
                UISeeCodePlugin.showError("Installation Error",
                        "The MetaWare debugger " + scdir
                                + "\nmay not be installed correctly or there is an internal error\n"
                                + "with one of its Debugger Options description files:\n\n"
                                + "Cannot find the debugger description file: " + x);
            }
        } catch (Exception x) {
            setErrorMessage(x.toString());
            String scdir = (String) fGUI.getEnvironment().getSymbolValue("SCDIR");
            if (scdir == null)
                scdir = "";
            else
                scdir = "(at " + scdir + ") ";
            UISeeCodePlugin.showError("Options File Exception",
                    "An unexpected exception occurred while reading a MetaWare Debugger\n" + scdir
                            + "Options Description file: " + x.toString());
        }
        Control[] children = scroller.getChildren();
        if (children.length > 0) { // should always be true
            // scroller.setMinSize(children[0].computeSize(SWT.DEFAULT,SWT.DEFAULT));
            // scroller.setContent(children[0]);
            constructOptionField(scroller);
        } else
            mOptions = null; // something messed up.
        mParentControl.layout(true);
        mCreated = true;
        mOriginalPropertyNames = fGUI.getPropertyNames(); // in case we need to revert.
        // If shell already rendered, then size may have increased; so we must
        // repack.
        // However, don't do it for Linux/GTK. For some reason, the dialog
        // just keeps getting wider and wider.
        // CORRECTON: packing just plain messes things up for some unknown reason.
        // The dialog gets pathalogically large on Windows with Eclipse 3.2
        // Just estimate a minimum size and reset the bounds without moving the
        // display, unless the display goes outside the screen
        if (mParentControl.getShell().isVisible()) {
            // NO. pack() has issues under Linux and under Windows with Eclipse 3.2
            // It causes the display to be pathologically large. Don't know why.
            // mParentControl.getShell().pack(true);
            Rectangle bounds = mParentControl.getShell().getBounds();
            Rectangle screenSize = mParentControl.getShell().getDisplay().getClientArea();
            boolean changed = false;
            if (bounds.width < MIN_PREFERRED_WIDTH) {
                bounds.width = Math.min(screenSize.width, MIN_PREFERRED_WIDTH);
                if (bounds.x + bounds.width > screenSize.width) {
                    bounds.x = screenSize.width - bounds.width;
                }
                changed = true;
            }
            if (bounds.height < MIN_PREFERRED_HEIGHT) {
                bounds.height = Math.min(screenSize.height, MIN_PREFERRED_HEIGHT);
                if (bounds.y + bounds.height > screenSize.height) {
                    bounds.y = screenSize.height - bounds.height;
                }
                changed = true;
            }
            if (changed)
                mParentControl.getShell().setBounds(bounds);
        }
        // printSizes(scroller,0);
    }

    /**
     * Contruct text field that displays seecode options; have it be now wider than the main dialog above it; it should
     * wrap.
     * @param mainDialog
     */
    private void constructOptionField(final Control mainDialog) {
        final Label label = new Label(mParentControl, SWT.LEFT);
        label.setText("Debugger Options: ");

        mOptions = new StyledText(mParentControl, SWT.MULTI | SWT.READ_ONLY | SWT.WRAP /* |SWT.V_SCROLL */);
        mOptions.setData("name", "debugger_options"); // For GUI tester
        // We have the option field updated by a
        // timing thread so as to only update it once when
        // there are multiple property changes.
        mOptionFieldUpdater = new OptionFieldUpdater();
        mOptionFieldUpdater.start();

        updateSeeCodeOptions();

    }

    /**
     * Check if we need to read the Guihili. True if it hasn't yet been read, or if our target CPU has changed.
     * @param config
     */
    private void createGuihiliIfNecessary(String launchName, IProject project, String target, File workingDir,
            String[] env) { // If
        if (fGUI != null && (fGUI.getEnvironment().getSymbolValue("MANAGED") == null
                || fGUI.getEnvironment().getSymbolValue("MANAGED").equals("0") == isManaged(project)
                || target != null && !target.equals(fTarget))) {
            fGUI.dispose();
            fGUI = null;
        }
        if (fGUI == null) {
            mInvalidPagePending = false;
            // If we're displaying Guihili from a previous
            // launch configuration, get rid of it.
            if (mParentControl != null) {
                Control prev[] = mParentControl.getChildren();
                for (int i = 0; i < prev.length; i++) {
                    prev[i].dispose();
                }
                if (mOptionFieldUpdater != null) {
                    mOptionFieldUpdater.terminate();
                    mOptionFieldUpdater = null;
                }
            }
        }
        if (target != null) {
            if (fGUI == null)
                createGuihili(launchName, project, target, workingDir, env);
        } else if (mParentControl != null)
            displayInvalidPage(mParentControl);
    }

    /**
     * Return whether or not the project of the executable being debugged is a "managed" project. If so, we grab target
     * information from the build properties.
     * @return whether or not the project of the executable being debugged is a "managed" project.
     */
    private boolean isManaged(IProject project) {
        if (project == null)
            return false;
        IProjectNature nature = null;
        try {
            // Prior to CDT 4.0, makefile projects were modelled as "non-managed".
            nature = project.getNature(ManagedCProjectNature.MNG_NATURE_ID);
            if (nature == null)
                return false;
        } catch (CoreException e) {
            // what to do?
            return false;
        }
        if (!ManagedBuildManager.manages(project))
            return false;
        // Now look if the project does indeed have the MetaWare toolchain. If not,
        // then we assume it to be a makefile project, and, therefore, is not managed.
        // FOLLOWING LOGIC COPIED FROM CDT code:
        IManagedBuildInfo mi = ManagedBuildManager.getBuildInfo(project);
        if (mi == null)
            return false;
        IConfiguration cfg = mi.getDefaultConfiguration();
        if (cfg != null && cfg.getBuilder() != null) {
            return cfg.getBuilder().isManagedBuildOn();
        }
        return false;
    }

    protected void setErrorMessage(String s) {
        fPermanentErrorMessage = s;
        fErrorSetter.setErrorMessage(s);
    }

    private Control focusedControl = null; // Control with the "focus"

    private String helpMessage = null;

    private static boolean isWindows() {
        return Platform.getOS().equals("win32");
    }

    /**
     * Detect if the conditions that cause cr96516 are present.
     * @param widget a widget that is about to get focus so that Help works.
     * @return true if the condition for cr96516 is present.
     */
    private static boolean hasSetFocusBug(Widget widget) {
        if (!(widget instanceof Button))
            return false;
        if (!isWindows())
            return false;
        Button button = (Button) widget;
        if ((button.getStyle() & SWT.RADIO) == 0)
            return false;
        if (button.getSelection())
            return false; // must not be set
        if (!(button.getParent() instanceof Group))
            return false;
        return true;
    }

    private static Button getRadioButtonWithSelection(Button button) {
        if (button.getSelection())
            return button;
        if (!(button.getParent() instanceof Group))
            return null;
        Group group = (Group) button.getParent();
        for (Control kid : group.getChildren()) {
            if (kid instanceof Button && (((Button) kid).getStyle() & SWT.RADIO) != 0
                    && ((Button) kid).getSelection())
                return (Button) kid;
        }
        return null;
    }

    private void createGuihili(String launchName, IProject project, final String target, File workingDir,
            String envStrings[]) {
        this.setErrorMessage(null);
        if (target == null)
            return;
        String scdir = EngineLocator.computeSCDIR(target, envStrings);
        IEnvironment env = EnvironmentFactory.create(workingDir, envStrings);
        final Gui g = new Gui(env, new OptFileResolver(scdir));
        g.setExceptionHandler(new IExceptionHandler() {

            @Override
            public void handleException(Throwable x) {
                SeeCodePlugin.log(x);
            }

            @Override
            public void handleException(String msg, Throwable x) {
                SeeCodePlugin.log(msg, x);

            }
        });

        g.setHelpHandler(new IHelpHandler() {

            @Override
            public void associateHelpMessage(IComponent c, final String msg) {
                if (c.getComponent() instanceof Control) {
                    final Control control = (Control) c.getComponent();
                    if (control.isDisposed())
                        return; // I don't know how, but this has been known to happen
                    control.getDisplay().asyncExec(new Runnable() {

                        private void addHelpListener(final Control widget, HelpListener listener) {
                            boolean added = false;
                            if (widget.isDisposed())
                                return; // Yes, it happens. Don't know how.
                            if (widget instanceof Composite) {
                                for (Control kid : ((Composite) widget).getChildren()) {
                                    addHelpListener(kid, listener);
                                    added = true;
                                }
                            }
                            if (!added) {
                                widget.addHelpListener(listener);
                                widget.addMouseTrackListener(new MouseTrackListener() {

                                    @Override
                                    public void mouseEnter(MouseEvent e) {
                                        focusedControl = widget;
                                        helpMessage = msg;
                                        // CR96516: setting focus to a radio button in a group incorrectly
                                        // selects the button under Windows! (Eclipse 3.3, 3.4)
                                        // So, undo the damage
                                        // <CR96516_HACK>
                                        if (hasSetFocusBug(widget)) {
                                            final Button buttonWithSelection = getRadioButtonWithSelection(
                                                    (Button) widget);
                                            widget.setFocus();
                                            if (buttonWithSelection != null) {
                                                control.getDisplay().asyncExec(new Runnable() {

                                                    @Override
                                                    public void run() {
                                                        ((Button) widget).setSelection(false);
                                                        if (!buttonWithSelection.getSelection()) {
                                                            buttonWithSelection.setSelection(true);
                                                            buttonWithSelection.notifyListeners(SWT.Selection,
                                                                    null);
                                                        }
                                                    }
                                                });
                                            }
                                        } else
                                            // </CR96516_HACK>
                                            widget.setFocus();
                                    }

                                    @Override
                                    public void mouseExit(MouseEvent e) {
                                        focusedControl = null;
                                    }

                                    @Override
                                    public void mouseHover(MouseEvent e) {
                                        // @todo Auto-generated method stub

                                    }
                                });
                            }
                        }

                        @Override
                        public void run() {

                            HelpListener helpListener = new HelpListener() {

                                @Override
                                public void helpRequested(HelpEvent e) {
                                    if (focusedControl != null) {
                                        HelpDialog.show(focusedControl, helpMessage);
                                    } else {
                                        HelpDialog.show(control, msg);
                                    }
                                }
                            };
                            addHelpListener(control, helpListener);

                        }
                    });
                }

            }
        });
        addSpecialPropertyListeners(g);
        fGUI = g;
        try {
            g.setProperty("whichOptions", "machine");
            g.setProperty("PROJECT", launchName);
            g.setProperty("SCXISS_WD",
                    project != null && project.getLocation() != null ? project.getLocation().toOSString()
                            : Utilities.getWorkspacePath());
            List<String> cores = new ArrayList<String>(Math.min(256, fProcessCount) + 1);
            cores.add("<Choose Core>");
            for (int i = 1; i <= fProcessCount; i++) {
                cores.add("" + i);
            }
            g.setProperty("CORE_LIST", cores);
        } catch (PropertyVetoException e) {
        }

        // IDE symbol is used to bypass GUI options
        env.putSymbolValue("IDE", "Eclipse");
        // We set "MANAGED" to "2" starting with IDE 8.4.0
        // In the future we may have guihili take into account such things.
        env.putSymbolValue("MANAGED", isManaged(project) ? "2" : "0");
        env.putSymbolValue("TARG", target.toUpperCase());
        String host = System.getProperty("os.name");
        env.putSymbolValue("HOST", host.indexOf("indows") > 0 ? "windows" : host.toLowerCase());
        env.putSymbolValue("SCDIR", scdir);
        env.putSymbolValue("CURRENT", "CURRENT_NOT_USED");
        env.putSymbolValue("XISS_SUPPORT", 1);
        // if (engine.build_id >= 1482)  // No engine is running at this point!! Assume it is latest.
        env.putSymbolValue("SCXISS_SUPPORT", 1);

        env.putSymbolValue("NO_DIR_XLATION", "1"); // IDE's directory translation is to be used
        setDirty(false);
        g.addPropertyChangeListener(new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent event) {
                // System.out.println("Property " + event.getPropertyName() +
                // "="
                // + event.getNewValue());
                // We're only interested in property changes
                // after the dialog is rendered. Otherwise, they are
                // side-effects of reading the guihili file and we
                // don't care about them.
                if (mCreated && !mPropertyChangeSuppressed) {
                    if (mOptions != null) {
                        // Cannot update immediately, because the
                        // transitive updates of properties isn't
                        // necessarily complete.
                        // Also, one widget click may cause multiple
                        // property changes. Thus, we delay the options field
                        // update for about 500 milliseconds until the
                        // dust settles.
                        mOptionFieldUpdater.update();

                    }
                }
                if (!isDirty() && !mPendingPropertyChange) {
                    // Prevent infinite recursion. The act of updating
                    // the buttons will cause "performApply" which
                    // causes this call to be made recursively.
                    setDirty(true);
                    // performApply is called as an unfortunate side-effect of
                    // updating the buttons. Therefore, set a flag to skip the
                    // call logic. See performApply below.
                    mPendingPropertyChange = true;
                    // We must call updateButtons after all property
                    // change listeners have executed, or else things
                    // will be in an indeterminant state.
                    if (mParentControl != null) { /* We may not be showing yet */
                        Display d = mParentControl.getDisplay();
                        if (d != null && fInvokeWhenFirstDirtied != null)
                            d.asyncExec(new Runnable() {

                                @Override
                                public void run() {
                                    try {
                                        if (!mParentControl.isDisposed())
                                            fInvokeWhenFirstDirtied.run();
                                    } finally {
                                        mPendingPropertyChange = false;
                                    }
                                }
                            });
                    }
                }
            }
        });
        if (mParentControl != null) {
            createControlActual();
        }
    }

    /* override */
    public void setDefaults(IGuihiliCallback configuration) {
        boolean dirty = isDirty();
        setDirty(true); // force regeneration
        setConfigurationFromGuihili(configuration, ISeeCodeOptionsAugmenter.PropState.DEFAULTS);
        if (!mCreated && mParentControl != null && fGUI != null) {
            createControlActual();
        }
        setDirty(dirty);
    }

    public String getProperty(String key) {
        return this.fGUI != null ? (String) fGUI.getProperty(key) : null;
    }

    private List<String> updateSeeCodeOptions() {
        // A configuration problem elsewhere could
        // prevent the options field from being created.
        if (mOptions == null)
            return null;
        // We may have a boundary case where the field
        // is being updated as the page is being closed...
        if (mOptions.isDisposed())
            return null;
        List<String> list = computeSeeCodeOptions();

        if (mLastSeeCodeOptions != null) {
            mOptions.setText("");
            ListAnalyzer a = new ListAnalyzer();
            a.analyze(list, mLastSeeCodeOptions);
            int cnt = a.getElementCount();
            for (int i = 0; i < cnt; i++) {
                String arg = a.getElement(i);
                int textSize = mOptions.getCharCount();
                if (a.followsDeletedElement(i)) {
                    if (textSize == 0) {
                        mOptions.append(" ");
                        textSize = 1;
                    }
                    mOptions.setStyleRange(new StyleRange(textSize - 1, 1, null, mBlue));
                }
                mOptions.append(StringUtil.arrayToArgString(new String[] { arg }));
                if (a.isNew(i)) {
                    mOptions.setStyleRange(new StyleRange(textSize, arg.length(), mBlue, null));
                }
                if (i + 1 < cnt)
                    mOptions.append(" ");
            }
        } else
            mOptions.setText(StringUtil.listToArgString(list));
        long thisTime = System.currentTimeMillis();
        // In case we have more than one update from a sequence
        // of property changes, don't update our "previous" list unless
        // time has elapsed.
        if (thisTime - mTimeOfLastOptionUpdate > 500) {
            mLastSeeCodeOptions = new ArrayList<String>(list);
            mTimeOfLastOptionUpdate = thisTime;
        }
        mOptions.layout(true);
        mOptions.getParent().layout(true);
        return list;
    }

    /**
     * @param configuration
     */
    private void setConfigurationFromGuihili(IGuihiliCallback configuration,
            ISeeCodeOptionsAugmenter.PropState state) {

        IProject project = configuration.getProject();
        String target = configuration.getTargetCPU();
        fProcessCount = configuration.getProcessCount();
        createGuihiliIfNecessary(configuration.getLaunchName(), project, target,
                configuration.getWorkingDirectory(), configuration.getEnvironment());
        fTarget = target;
        String errorKey = "CONFIG";
        error(errorKey, null);
        if (fGUI == null) // target processor not yet known; debug dialog will be blank
            return;
        try {
            applyBuildOptions(fGUI, state, project);
        } catch (ConfigurationException e1) {
            error(errorKey, e1.getMessage());
        }

        Collection<String> names = fGUI.getPropertyNames();
        List<String> swahiliArgs = configuration.getSwahiliArguments();
        if (isDirty() || swahiliArgs == null || swahiliArgs.size() == 0) {
            List<String> options = computeSeeCodeOptions();
            configuration.setSwahiliArguments(options);
        }

        // We must encode as map of strings; that's
        // the only kind that Eclipse configuration
        // stuff will take.
        Properties props = PropertyStorage.encode(names, fGUI);
        configuration.setProperties(props);

        // <HACK>
        // The xISS generates secret source files in ".xiss*" directories. These must be filtered
        // so that they don't mistakenly get used as source files.
        // We go ahead and add the exclusion filter unconditionally to the project. Even if the
        // user cancels this operation, or later retargets to non-xISS, the filter benignly remains.
        // If there is a better way of doing this, then by all means, change this.
        if (props != null && project != null && "XISS".equals(props.get("ARC_target"))) {
            excludeSourceFilesFrom(project, ".xiss*/**");
        }
        // </HACK>
    }

    /**
     * Given a project and an exclusion filter, append the filter to the list of patterns to be exluded as source
     * directories, unless the pattern is already there.
     * @param project the associated project.
     * @param filter the source-path pattern to excluce.
     */
    private static void excludeSourceFilesFrom(IProject project, String filter) {
        ICProjectDescriptionManager mngr = CoreModel.getDefault().getProjectDescriptionManager();
        ICProjectDescription des = mngr.getProjectDescription(project, false);
        if (des != null) {
            ICConfigurationDescription cfgd = des.getDefaultSettingConfiguration();
            IPath pattern = new Path(filter);
            if (!doesContainExclusionPattern(project, cfgd, pattern)) {
                des = mngr.getProjectDescription(project, true); // get writable version
                ICConfigurationDescription cfgds[] = des.getConfigurations();
                for (ICConfigurationDescription c : cfgds) {
                    appendExclusionPattern(project, c, pattern);
                }
                try {
                    mngr.setProjectDescription(project, des); // required to flush cache of old stuff
                } catch (CoreException e) {
                    UISeeCodePlugin.log(e);
                }
            }
        }
    }

    private static boolean doesContainExclusionPattern(IProject project, ICConfigurationDescription cfgd,
            IPath pattern) {
        ICSourceEntry srcEntries[] = cfgd.getSourceEntries();
        boolean found = false;
        for (ICSourceEntry e : srcEntries) {
            if (project.getFullPath().equals(e.getFullPath())) {
                for (IPath p : e.getExclusionPatterns()) {
                    if (p.equals(pattern)) {
                        found = true;
                        break;
                    }
                }
            }
        }
        return found;
    }

    private static void appendExclusionPattern(IProject project, ICConfigurationDescription cfgd, IPath pattern) {
        if (!doesContainExclusionPattern(project, cfgd, pattern)) {
            ICSourceEntry srcEntries[] = cfgd.getSourceEntries();
            ICSourceEntry e = null;
            for (ICSourceEntry entry : srcEntries) {
                if (project.getFullPath().equals(entry.getFullPath())) {
                    e = entry;
                    break;
                }
            }
            if (e == null) {
                ICSourceEntry newSrcEntries[] = new ICSourceEntry[srcEntries.length + 1];
                System.arraycopy(srcEntries, 0, newSrcEntries, 0, srcEntries.length);
                newSrcEntries[newSrcEntries.length - 1] = new CSourceEntry(project.getFullPath(),
                        new IPath[] { pattern }, 0);
                try {
                    cfgd.setSourceEntries(newSrcEntries);
                } catch (Exception e1) {
                    UISeeCodePlugin.log(e1);
                }
            } else {
                IPath patterns[] = e.getExclusionPatterns();
                IPath newPatterns[] = new IPath[patterns.length + 1];
                System.arraycopy(patterns, 0, newPatterns, 0, patterns.length);
                newPatterns[newPatterns.length - 1] = pattern;
                ICSourceEntry newE = new CSourceEntry(project.getFullPath(), newPatterns, 0);
                for (int i = 0; i < srcEntries.length; i++) {
                    if (srcEntries[i] == e) {
                        srcEntries[i] = newE;
                    }
                }
                try {
                    cfgd.setSourceEntries(srcEntries);
                } catch (Exception e1) {
                    UISeeCodePlugin.log(e1);
                }
            }
        }
    }

    /**
     * Compute SeeCode options to passed to Swahili
     */
    @SuppressWarnings("unchecked")
    private List<String> computeSeeCodeOptions() {
        // the "OK" action causes the ARG_ACTION property
        // to be set.
        Action action = fGUI.getAction(Gui.GEN_ARG_ACTION);
        if (action != null && isDirty()) {
            ArrayList<String> targs = new ArrayList<String>();
            try {
                // First will be "-targs=XXX"
                targs.add("-targs=" + fTarget.toUpperCase());
                // Don't issue property change from this action.
                mPropertyChangeSuppressed = true;
                fGUI.setProperty("ARG_ACTION", targs);
                // HACK: an old anchronism - "ACTION" is
                // a argument list that is appended to each
                // time "OK" action is fired.
                fGUI.setProperty("ACTION", null);
                action.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, Gui.GEN_ARG_ACTION));
            } catch (PropertyVetoException e) {
                e.printStackTrace();
            } finally {
                mPropertyChangeSuppressed = false;
            }
        }
        /*
         * else setErrorMessage("No OK action");
         */

        List<String> list = (List<String>) fGUI.getProperty("ARG_ACTION");
        List<String> slist;
        if (list != null) { // will be null if invoked from on-the-fly launcher.
            slist = new ArrayList<String>(list.size());
            slist.addAll(list);
        } else {
            // On the fly launch; all guihili defaults; just specify target.
            slist = new ArrayList<String>();
            slist.add("-targs=" + fTarget.toUpperCase());
            slist.add("-toggle=include_local_symbols=1");
            slist.add("-profile");
        }
        if (mSeeCodeOptionsAugmenter != null) {
            // Augment arguments from build config settings
            // (e.g., "-a7")
            String[] args = slist.toArray(new String[slist.size()]);
            args = mSeeCodeOptionsAugmenter.augmentArguments(args);
            slist = Arrays.asList(args);
        }
        return slist;
    }

    private static boolean containsSameElements(Collection<String> a, Collection<String> b) {
        return a != null && b != null && a.size() == b.size() && a.containsAll(b);
    }

    // private static void P(String name, Collection<? extends Object>col){
    // System.out.println();
    // System.out.println(name);
    // List<String> list = new ArrayList<String>(col.size());
    // for (Object s: col){
    // list.add((String)s);
    // }
    // Collections.sort(list);
    // for (String s: list){
    // System.out.println(s);
    // }
    // }

    /* override */
    public void initializeFrom(IGuihiliCallback configuration) {
        List<String> originalSwahiliArgs = configuration.getSwahiliArguments();
        IProject project = configuration.getProject();
        fTarget = configuration.getTargetCPU();
        fProcessCount = configuration.getProcessCount();
        if (fTarget != null) {
            createGuihiliIfNecessary(configuration.getLaunchName(), project, fTarget,
                    configuration.getWorkingDirectory(), configuration.getEnvironment());
        } else
            fGUI = null;
        if (fGUI == null) {
            setDirty(false);
            return; // target CPU not recognized
        }
        try {
            mSeeCodeOptionsAugmenter = isManaged(project) ? SeeCodeOptions.readOptionMapping(fTarget, project)
                    : null;
            if (mSeeCodeOptionsAugmenter != null) { // establish defaults 
                this.mSeeCodeOptionsAugmenter.augmentDefaults(fGUI.getPropertyMap());
            }
        } catch (ConfigurationException e1) {
            setErrorMessage(e1.getMessage());
        }
        try {
            Properties props = configuration.getProperties();
            if (props != null) {
                // P("GUI before",fGUI.getPropertyMap().keySet());
                // P("props",props.keySet());
                // P("Original",mOriginalPropertyNames);
                // Get rid of all new properties so that they will default.
                if (mOriginalPropertyNames != null) {
                    // Map<String,Object> map = fGUI.getPropertyMap();
                    // "retainAll" doesn't fire change listeners
                    // map.keySet().retainAll(mOriginalPropertyNames);
                    Collection<String> names = fGUI.getPropertyNames();
                    List<String> exclude = new ArrayList<String>(names);
                    exclude.removeAll(mOriginalPropertyNames);
                    for (String s : exclude) {
                        try {
                            fGUI.setProperty(s, null);
                        } catch (PropertyVetoException e) {
                        }
                    }
                }
                // P("GUI after retaining",fGUI.getPropertyMap().keySet());
                boolean savePendingPropertyChange = mPropertyChangeSuppressed;
                try {
                    mPropertyChangeSuppressed = true; // suppress property changes tuff
                    PropertyStorage.decode(fGUI, props);
                } finally {
                    mPropertyChangeSuppressed = savePendingPropertyChange;
                }
                // P("GUI afterwards",fGUI.getPropertyMap().keySet());
            }
            applyBuildOptions(fGUI, props == null || props.size() == 0 ? ISeeCodeOptionsAugmenter.PropState.DEFAULTS
                    : ISeeCodeOptionsAugmenter.PropState.LOADING, project);
        } catch (ConfigurationException e) {
            setErrorMessage(e.getMessage());
        }
        if (mParentControl != null && !mCreated) {
            createControlActual();
        }
        setDirty(true); // Force ARG_ACTION regeneration
        updateSeeCodeOptions();
        // If the project properties changed, then those arguments that are dependent on project properties may
        // have also changed.
        setDirty(!containsSameElements(computeSeeCodeOptions(), originalSwahiliArgs));
    }

    /**
     * Extract the target-specific compiler options (e.g. "-arc700") and set properties appropriately.
     * @param gui the guihili whose property values are to be updated.
     * @param launchConfig the corresponding launch configuration
     * @param applyingDefaults true if this is the initial defaults values of all properties.
     * @throws ConfigurationException
     */
    private void applyBuildOptions(Gui gui, ISeeCodeOptionsAugmenter.PropState state, IProject project)
            throws ConfigurationException {
        if (isManaged(project)) {
            ISeeCodeOptionsAugmenter aug = SeeCodeOptions.readOptionMapping(fTarget, project);
            if (aug != null)
                aug.augmentProperties(gui.getPropertyMap(), state);
            // else there is no correlation between build options and
            // debugger options.

            // If the project against which we are building was imported from
            // an old MetaDeveloper project space, retrieve the SeeCode options
            if (state == ISeeCodeOptionsAugmenter.PropState.DEFAULTS) {
                if (project != null) {
                    String configID = ManagedBuildManager.getBuildInfo(project).getDefaultConfiguration().getId();
                    Map<String, String> map = UISeeCodePlugin.getDefault().getDefaultSeeCodeOptions(project,
                            configID);
                    if (map != null) {
                        for (Map.Entry<String, String> entry : map.entrySet()) {
                            try {
                                fGUI.setProperty(entry.getKey(), entry.getValue());
                            } catch (PropertyVetoException e) {
                            }
                        }
                    }
                }

            }
        }
    }

    /* override */
    public void performApply(IGuihiliCallback configuration) {
        setConfigurationFromGuihili(configuration, ISeeCodeOptionsAugmenter.PropState.UPDATING);
        setDirty(false);
    }

    public void performOK() {
        if (fGUI != null) {
            // OK action may pop up errors or warnings...
            Action okAction = fGUI.getAction("OK");
            if (okAction != null) {
                okAction.actionPerformed(new ActionEvent(fGUI, ActionEvent.ACTION_PERFORMED, "OK"));
            }
        }
    }

    /**
     * A custom layout so that the SeeCode options field doesn't contribute to the total width of the dialog, since the
     * options field is suppose to wrap.
     * @author pickens
     * @currentOwner <a href="mailto:pickens@arc.com">pickens</a>
     * @version $Revision$
     * @lastModified $Date$
     * @lastModifiedBy $Author$
     * @reviewed 0 $Revision:1$
     */
    static class MyLayout extends Layout {

        private static final int VERTICAL_SPACING = 4;

        private static final int HORIZONTAL_MARGIN = 4;

        private static final int VERTICAL_MARGIN = 4;

        private static final int HORIZONTAL_SPACING = 4;

        private int _optionsFieldHeight = 0;

        private int _mainDialogHeight = -1;

        @Override
        protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
            int width = 0;
            int height = 0;
            _optionsFieldHeight = 0;
            Control mainDialog = null;
            Control optionsLabel = null;
            Control optionsField = null;
            for (Control kid : composite.getChildren()) {
                if (kid instanceof Label)
                    optionsLabel = kid;
                else if (kid instanceof StyledText)
                    optionsField = kid;
                else
                    mainDialog = kid;
            }
            if (mainDialog != null) {
                //NOTE: launch config dialog updates too frequently for no reason. 
                // Since Guihili for ARC is large, it causes a very noticeable delay. So,
                // ignore the fact that "flushCache" is true. (cr100049)
                Point size = mainDialog.computeSize(wHint, hHint, false/*flushcache*/);
                width = size.x;
                height = size.y;
                _mainDialogHeight = height;
            }
            int optionsFieldWidth = width;
            if (optionsLabel != null) {
                optionsFieldWidth = width - optionsLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT, false).x
                        - HORIZONTAL_SPACING;
            }
            if (optionsField != null) {
                if (optionsFieldWidth <= 0)
                    optionsFieldWidth = SWT.DEFAULT;
                Point size = optionsField.computeSize(optionsFieldWidth, SWT.DEFAULT, flushCache);
                // Fudge optionsfield height by 4. Linux/GTK version seems to compute a height that is too short.
                _optionsFieldHeight = size.y + 4;
            }

            return new Point(width + HORIZONTAL_MARGIN * 2,
                    _optionsFieldHeight + _mainDialogHeight + VERTICAL_SPACING + VERTICAL_MARGIN * 2);
        }

        @Override
        protected void layout(Composite composite, boolean flushCache) {
            if (flushCache) {
                _mainDialogHeight = 0;
                _optionsFieldHeight = 0;
            }
            Rectangle area = composite.getClientArea();

            area.x += HORIZONTAL_MARGIN;
            area.width -= HORIZONTAL_MARGIN;
            area.y += VERTICAL_MARGIN;
            area.height -= VERTICAL_MARGIN;
            Control mainDialog = null;
            Control optionsLabel = null;
            Control optionsField = null;
            for (Control kid : composite.getChildren()) {
                if (kid instanceof Label)
                    optionsLabel = kid;
                else if (kid instanceof StyledText)
                    optionsField = kid;
                else
                    mainDialog = kid;
            }

            if (_mainDialogHeight <= 0 && mainDialog != null) {
                _mainDialogHeight = mainDialog.computeSize(area.width, SWT.DEFAULT, flushCache).y;
            }
            if (_optionsFieldHeight <= 0 && optionsLabel != null) {
                Point labelSize = optionsLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT, false);
                _optionsFieldHeight = labelSize.y;
                int labelWidth = labelSize.x;
                // Fudge optionsfield hieght by 4. Linux/GTK version seems to compute a height that is too short.
                if (optionsField != null) {
                    int fieldWidth = area.width - labelWidth - HORIZONTAL_SPACING;
                    if (fieldWidth > 0)
                        _optionsFieldHeight = Math.max(
                                optionsField.computeSize(fieldWidth, SWT.DEFAULT, false).y + 4,
                                _optionsFieldHeight);
                }
            }
            int dialogHeight = area.height - _optionsFieldHeight - VERTICAL_SPACING;
            if (dialogHeight > _mainDialogHeight)
                dialogHeight = _mainDialogHeight;
            if (mainDialog != null) {
                mainDialog.setBounds(area.x, area.y, area.width, dialogHeight);
            }
            int labelWidth = 0;
            int yCoord = area.y + dialogHeight + VERTICAL_SPACING;
            int fieldHeight = area.height - yCoord;
            if (optionsLabel != null) {
                Point labelSize = optionsLabel.computeSize(SWT.DEFAULT, _optionsFieldHeight, false);
                labelWidth = labelSize.x;
                optionsLabel.setBounds(area.x, yCoord, labelSize.x, fieldHeight);
            }
            if (optionsField != null) {
                optionsField.setBounds(area.x + labelWidth + HORIZONTAL_SPACING, yCoord,
                        area.width - labelWidth - HORIZONTAL_SPACING, fieldHeight);
            }

        }

    }

    private static Pattern startPattern = Pattern.compile("XISS_bridge_(\\d+)_start");

    private static Pattern targetPattern = Pattern.compile("XISS_bridge_(\\d+)_target_address");

    private static Pattern lengthPattern = Pattern.compile("XISS_bridge_(\\d+)_length");

    private static Pattern cpuPattern = Pattern.compile("XISS_bridge_(\\d+)_target");

    private static long toInteger(Object v) {
        if (v instanceof Number)
            return ((Number) v).longValue();
        if (v instanceof String) {
            String s = v.toString();
            if (s.startsWith("0x") || s.startsWith("0X"))
                return Long.parseLong(s.substring(2), 16);
            return Long.parseLong(s);
        }
        throw new NumberFormatException("Not a valid number " + v);
    }

    /**
     * Handle special properties (e.g., xISS Bridge stuff).
     * @param g
     */
    private void addSpecialPropertyListeners(Gui g) {
        handleXissStuff(g);
    }

    private Map<String, String> pendingErrors = null;

    private void error(String property, String msg) {
        if (msg != null && pendingErrors == null) {
            pendingErrors = new HashMap<String, String>(3);
        }
        if (pendingErrors != null) {
            if (msg == null)
                pendingErrors.remove(property);
            else
                pendingErrors.put(property, msg);
            if (fPermanentErrorMessage == null) {
                if (pendingErrors.size() == 0) {
                    fErrorSetter.setErrorMessage(null);
                } else {
                    for (String s : pendingErrors.values()) {
                        fErrorSetter.setErrorMessage(s);
                        break;
                    }
                }
            }
        }
    }

    /**
     * Return the XISS bridge target corresponding to nothing. Originally it was "<Choose Core>". But in case it
     * changes, we find the alternate name.
     */
    private static String computeDisabledBridgeTarget(Gui g) {
        String tryIt = (String) g.getProperty("XISS_bridge_5_target");
        if (tryIt != null && tryIt.indexOf("oose") > 0)
            return tryIt;
        tryIt = (String) g.getProperty("XISS_bridge_7_target");
        if (tryIt != null && tryIt.indexOf("oose") > 0)
            return tryIt;
        tryIt = (String) g.getProperty("XISS_bridge_3_target");
        if (tryIt != null && tryIt.indexOf("oose") > 0)
            return tryIt;
        return "<Choose Core>";
    }

    private void handleXissStuff(final Gui g) {
        g.addPropertyChangeListener(new PropertyChangeListener() {

            private Set<String> boundProperties = new HashSet<String>();

            private boolean checkValue(String what, final PropertyChangeEvent evt) {
                long ivalue = 0;
                String errorKey = evt.getPropertyName();
                error(errorKey, null);
                try {
                    ivalue = toInteger(evt.getNewValue());
                    // A "8192" may be converting to "0x2000", which is OK.
                    // But we get into an error loop if "0" is being converted to "0x0".
                    if (evt.getOldValue() != null && ivalue == toInteger(evt.getOldValue()))
                        return false;
                } catch (NumberFormatException x) {
                    error(errorKey, what + " must be an integer multiple of 8192");
                    // restoreOldValue(evt);
                    return false;
                }

                if (ivalue % 0x2000 != 0 || ivalue == 0 && evt.getPropertyName().indexOf("length") > 0) {
                    error(errorKey, what + " must be a page (8K) multiple.");
                    // restoreOldValue(evt);
                    return false;
                }

                return true;
            }

            // /**
            // * @param g
            // * @param evt
            // * @throws PropertyVetoException
            // */
            // private void restoreOldValue(final PropertyChangeEvent evt)
            // throws PropertyVetoException {
            // PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable(){
            //
            // public void run() {
            // try {
            // g.setProperty(evt.getPropertyName(), evt.getOldValue());
            // } catch (PropertyVetoException e) {
            // }
            // }});
            //                
            // }

            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                // Deal with xISS bridge. When start address is set,
                // set the target address to it; and vice-versa.
                String name = evt.getPropertyName();
                boundProperties.remove(name); // no longer bound once modified.

                if (name.charAt(0) == 'X' && name.startsWith("XISS_bridge_")) {
                    Matcher m = startPattern.matcher(name);
                    String otherProperty = null;
                    if (m.matches()) {
                        if (!checkValue("Bridge start address", evt))
                            return;
                        otherProperty = "XISS_bridge_" + m.group(1) + "_target_address";
                    } else {
                        m = targetPattern.matcher(name);
                        if (m.matches()) {
                            if (!checkValue("Bridge target address", evt))
                                return;
                            otherProperty = "XISS_bridge_" + m.group(1) + "_start";
                        } else {
                            m = lengthPattern.matcher(name);
                            if (m.matches()) {
                                checkValue("Bridge length", evt);
                            } else if (pendingErrors != null) {
                                m = cpuPattern.matcher(name);
                                if (m.matches()) {
                                    if (((String) evt.getNewValue()).indexOf("Choose") >= 0) {
                                        // This bridge was just disabled. Remove an pending errors
                                        // related to it.
                                        String which = m.group(1);
                                        String start = "XISS_bridge_" + which + "_start";
                                        String target = "XISS_bridge_" + which + "_target_address";
                                        String length = "XISS_bridge_" + which + "_length";
                                        try {
                                            if (pendingErrors.get(start) != null) {
                                                g.setProperty(start, "0x0");
                                            }
                                            if (pendingErrors.get(target) != null) {
                                                g.setProperty(target, "0x0");
                                            }
                                            if (pendingErrors.get(length) != null) {
                                                g.setProperty(length, "0x2000");
                                            }
                                        } catch (PropertyVetoException e) {
                                            // shouldn't get here.
                                        }
                                    }
                                }
                            }
                        }
                    }
                    if (otherProperty != null) {
                        if (!boundProperties.contains(otherProperty)) {
                            if ("0x0".equals(g.getProperty(otherProperty))) {
                                boundProperties.add(otherProperty);
                            }
                        }
                        if (boundProperties.contains(otherProperty)) {
                            try {
                                g.setProperty(otherProperty, evt.getNewValue());
                            } catch (PropertyVetoException e) {

                            } finally {
                                boundProperties.add(otherProperty); // restore it in case it was removed.
                            }
                        }
                    }
                }
                // If switching to non-xISS, then clear errors (cr96254) that are pending.
                // We assign valid values to erroneous entries, then disable bridge target
                // in case they re-enable xISS.
                // We must not do this is this thread because of setting property X to A triggers
                // a setting of property X to B will cause an infinite recursive loop in the
                // property change listener stuff.
                String target = (String) g.getProperty("ARC_target");
                if (!"XISS".equals(target) && pendingErrors != null && pendingErrors.size() > 0) {
                    mParentControl.getDisplay().asyncExec(new Runnable() {

                        @Override
                        public void run() {
                            if (pendingErrors == null)
                                return; // in case of race condition
                            String disabledBridgeTarget = computeDisabledBridgeTarget(g); // e.g. "<Choose Core>"
                            for (Map.Entry<String, String> entry : pendingErrors.entrySet()) {
                                final String key = entry.getKey();

                                if (key.startsWith("XISS_bridge_")) {
                                    error(key, null);
                                    try {
                                        g.setProperty(key, key.endsWith("length") ? "0x2000" : "0x0");
                                    } catch (PropertyVetoException e1) {
                                    } // give a valid value
                                    int i = key.indexOf('_', 12);
                                    if (i > 0) {
                                        String coreSpec = key.substring(0, i) + "_target";
                                        // Disable faulty bridge spec
                                        try {
                                            g.setProperty(coreSpec, disabledBridgeTarget);
                                        } catch (PropertyVetoException e) {
                                        }
                                    }
                                }
                            }

                        }
                    });
                }
            }
        });
    }
}