/*
* JFLAP - Formal Languages and Automata Package
*
*
* Susan H. Rodger
* Computer Science Department
* Duke University
* August 27, 2009
* Copyright (c) 2002-2009
* All rights reserved.
* JFLAP is open source software. Please see the LICENSE for terms.
*
*/
package gui.environment;
import file.Encoder;
import gui.action.MultipleSimulateAction.MultiplePane;
import gui.environment.tag.EditorTag;
import gui.environment.tag.Satisfier;
import gui.environment.tag.Tag;
import java.awt.*;
import java.io.File;
import java.io.Serializable;
import java.util.*;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import debug.EDebug;
import sun.security.util.Debug;
/**
* The environment class is the central view that manages various "hangers on"
* of an object. By "hanger on" I mean a component that has some relevance to
* the object that this environment contains. For example, each Environment
* instance has, at minimum some sort of component whereby this structure can be
* edited. The Environment
instance keeps track of and displays
* these various components.
*
* @see gui.environment.EnvironmentFrame
* @see gui.environment.tag
*
* @author Thomas Finley
*/
public abstract class Environment extends JPanel {
/**
* Instantiates a new environment for the given object. This environment is
* assumed to have no native file to which to save the object. One should
* use the setFile
object if this environment should have
* one.
*
* @param object
* assumed to be some sort of object that this environment holds;
* subclasses may provide more stringent requirements for this
* kind of object
*/
public Environment(Serializable object) {
theMainObject = object;
clearDirty();
initView();
}
/**
* Returns the main object for this environment. This is the object that was
* passed in for the constructor.
*
* @return the main object for this environment
*/
public Serializable getObject() {
return theMainObject;
}
/**
* Adds a file change listener to this environment.
*
* @param listener
* the listener to add
*/
public void addFileChangeListener(FileChangeListener listener) {
fileListeners.add(listener);
}
/**
* Removes a file change listener from this environment.
*
* @param listener
* the listener to remove
*/
public void removeFileChangeListener(FileChangeListener listener) {
fileListeners.remove(listener);
}
/**
* Distributes the given file change event among all file change listeners.
*
* @param event
* the file change event to distribute
*/
protected void distributeFileChangeEvent(FileChangeEvent event) {
Iterator it = fileListeners.iterator();
while (it.hasNext()) {
FileChangeListener listener = (FileChangeListener) it.next();
listener.fileChanged(event);
}
}
/**
* Returns the file that this Environment
has loaded itself
* from.
*
* @return the file object that is owned by this environment as the place to
* store the serializable object, or null
if this
* environment currently has no file
*/
public File getFile() {
return file;
}
/**
* Sets the file owned by this Environment
as the default
* location to save the object.
*
* @param file
* the new file for the environment
*/
public void setFile(File file) {
File oldFile = this.file;
this.file = file;
distributeFileChangeEvent(new FileChangeEvent(this, oldFile));
}
public void setMultipleObjects(ArrayList objects) {
this.myObjects = objects;
}
/**
* Sets the encoder to use when writing this environment's file. This should
* be set when the file is ever written, or when a file is read and the
* format it was read in has a corresponding encoder.
*
* @param encoder
* the encoder for this
*/
public void setEncoder(Encoder encoder) {
this.encoder = encoder;
}
/**
* Gets the encoder to be used when saving this file.
*
* @return the encoder to use to save this file, or null
if
* no encoder has been chosen yet
*/
public Encoder getEncoder() {
return encoder;
}
/**
* A helper function to set up the GUI components.
*/
private void initView() {
this.setLayout(new BorderLayout());
tabbed = new JTabbedPane();
super.add(tabbed, BorderLayout.CENTER);
// So that when the user changes the view by clicking in the
// tabbed pane, this knows about it.
tabbed.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent event) {
distributeChangeEvent();
}
});
}
/**
* Adds a new component to the environment. Presumably this added component
* has some relevance to the current automaton or grammar held by the
* environment, though this is not strictly required.
*
* @param component
* the component to add, which should be unique for this
* environment
* @param name
* the name this component should be labeled with, which is not
* necessarily a unique label
* @param tags
* the tags associated with the component, or just a raw Tag
* implementor if this component has no special tags associated
* with it
* @see gui.environment.tag
*/
public void add(Component component, String name, Tag tags) {
componentTags.put(component, tags);
tabbed.addTab(name, component);
// Takes care of the deactivation of EditorTag tagged
// components in the event that such action is appropriate.
if (tags instanceof gui.environment.tag.CriticalTag) {
criticalObjects++;
if (criticalObjects == 1)
setEnabledEditorTagged(false);
}
distributeChangeEvent();
}
/**
* Returns if a particular component is part of this environment, as through
* addition through one of the add
methods
*
* @param component
* the component to check for membership in this environment
* @see #add
* @see #remove
*/
public boolean contains(Component component) {
return tabbed.indexOfComponent(component) != -1;
}
/**
* Deactivates or activates editor tagged objects in this environment.
*
* @param enabled
* true
if editor tagged objects should be
* enabled, false
if editor tagged objects should
* be disabled
*/
public void setEnabledEditorTagged(boolean enabled) {
for (int i = 0; i < tabbed.getTabCount(); i++) {
Component c = tabbed.getComponentAt(i);
if (((Tag) componentTags.get(c)) instanceof EditorTag)
tabbed.setEnabledAt(i, enabled);
}
}
/**
* Adds a component with the specified name. This is the same as the other
* add method, except without that tag field, which is assumed to be a tag
* object with no other tagness ascribed to it (i.e. a generic tag). That
* is, the component is assumed to have an empty tag.
*
* @param component
* the component to add, which should be unique for this
* environment
* @param name
* the name this component should be labeled with, which is not
* necessarily a unique label
* @see #add(Component, String, Tag)
*/
public void add(Component component, String name) {
this.add(component, name, new Tag() {
});
}
/**
* Programmatically sets the currently active component in this environment.
*
* @param component
* the component to make active
* @see #getActive
*/
public void setActive(Component component) {
tabbed.setSelectedComponent(component);
// The change event should be automatically distributed by the
// model of the tabbed pane
}
/**
* Returns the currently active component in this environment.
*
* @return the currently active component in this environment
* @see #setActive
*/
public Component getActive() {
return tabbed.getSelectedComponent();
}
/**
* Returns whether or not the component is enabled, that is, selectable.
*
* @param component
* the component to check for enabledness
* @return true
if the given component is enabled, false
* if the given component is disabled
*/
public boolean isEnabled(Component component) {
return tabbed.isEnabledAt(tabbed.indexOfComponent(component));
}
/**
* Sets whether or not a component is enabled.
*
* @param component
* the component to change the enabledness
* @param enabled
* true
if the component should be made enabled,
* false
if it should be made disabled
*/
public void setEnabled(Component component, boolean enabled) {
tabbed.setEnabledAt(tabbed.indexOfComponent(component), enabled);
distributeChangeEvent();
}
/**
* Adds a change listener to this object. The listener will receive events
* whenever the active component changes, or when components are made
* enabled or disabled, or when components are added or removed.
*
* @param listener
* the listener to add
*/
public void addChangeListener(ChangeListener listener) {
changeListeners.add(listener);
}
/**
* Removes a change listener from this object.
*
* @param listener
* the listener to remove
*/
public void removeChangeListener(ChangeListener listener) {
changeListeners.remove(listener);
}
/**
* Distributes a change event to all listeners.
*/
protected void distributeChangeEvent() {
ChangeEvent e = new ChangeEvent(this);
Iterator it = (new HashSet(changeListeners)).iterator();
while (it.hasNext())
((ChangeListener) it.next()).stateChanged(e);
}
/**
* Removes a component from this environment.
*
* @param component
* the component to remove
*/
public void remove(Component component) {
tabbed.remove(component);
Tag tag = (Tag) componentTags.remove(component);
// Takes care of the deactivation of EditorTag tagged
// components in the event that such action is appropriate.
if (tag instanceof gui.environment.tag.CriticalTag) {
criticalObjects--;
if (criticalObjects == 0)
setEnabledEditorTagged(true);
}
distributeChangeEvent();
}
/**
* Returns the tag for a given component, provided this tag is in the
* component.
*
* @param component
* the component to get the tag for
* @return the tag for the component
*/
public Tag getTag(Component component) {
return (Tag) componentTags.get(component);
}
/**
* Returns an array containing all components.
*
* @return an array containing all components.
*/
public Component[] getComponents() {
Component[] comps = new Component[tabbed.getTabCount()];
for (int i = 0; i < comps.length; i++)
comps[i] = tabbed.getComponentAt(i);
return comps;
}
/**
* Returns an array whose components and tags satisfy the given satisfier.
*
* @param satisfier
* the satisfier all components and their tags must satisfy
* @return an array containing all those components who, along with their
* tags, satisfied the satisfier
*/
public Component[] getComponents(Satisfier satisfier) {
ArrayList list = new ArrayList();
for (int i = 0; i < tabbed.getTabCount(); i++) {
Component c = tabbed.getComponentAt(i);
if (satisfier.satisfies(c, (Tag) componentTags.get(c)))
list.add(c);
}
return (Component[]) list.toArray(new Component[0]);
}
/**
* Detects if there are any components in this environment that satisfy the
* given satisfier. This method works in time linear in the number of
* components in this environment.
*
* @param satisfier
* the satisfier to check components and their tags against
* @return true
if the satisfier has managed to match at
* least one object, false
if none of the objects in
* this satisfier are matched
*/
public boolean isPresent(Satisfier satisfier) {
for (int i = 0; i < tabbed.getTabCount(); i++) {
Component c = tabbed.getComponentAt(i);
if (satisfier.satisfies(c, (Tag) componentTags.get(c)))
return true;
}
return false;
}
/**
* Returns if this environment dirty. An environment is called dirty if the
* object it holds has been modified since the last save.
*
* @return true
if the environment is dirty, false
* otherwise
*/
public boolean isDirty() {
return dirty;
}
/**
* Sets the dirty bit. This should be called if the object is changed.
*/
public void setDirty() {
// EDebug.print("Change has come");
dirty = true;
}
/**
* Clears the dirty bit. This should be called when the object is saved to a
* file, or is in some other such state that a save is not required.
*/
public void clearDirty() {
dirty = false;
}
public void setNewMainObject(Serializable obj){
theMainObject = obj;
}
public void resizeSplit(){
//super.setSize(width, height);
if(myObjects != null && this.tabbed != null){
if(myObjects.size() > 0 && this.tabbed.getTabCount() == 1){
Component cur = this.getActive();
if(cur instanceof MultiplePane){
MultiplePane mult = (MultiplePane)cur;
mult.mySplit.setDividerLocation(.5);
}
}
}
}
/**For Testing multiple objects*/
public ArrayList myObjects;
public ArrayList myTestStrings;
public ArrayList myTransducerStrings;
/** The encoder for this document. */
private Encoder encoder = null;
/** The mapping of components to their respective tag objects. */
private HashMap componentTags = new HashMap();
/** The tabbed pane for this environment. */
public JTabbedPane tabbed;
/** The collection of change listeners for this object. */
private transient HashSet changeListeners = new HashSet();
/** The object that this environment centers on. */
private Serializable theMainObject;
/** The file owned by this serializable object. */
private File file;
/** The collection of file change listeners. */
private Set fileListeners = new HashSet();
/**
* The number of "CriticalTag" tagged components. Hokey but fast.
*/
private int criticalObjects = 0;
/** The dirty bit. */
private boolean dirty = false;
}