Ptidej-v5.2-master/POM Tests/data/ArgoUML_0.28/src/org/argouml/cognitive/Agency.java
// $Id: Agency.java 16299 2008-12-07 21:18:09Z tfmorris $
// Copyright (c) 1996-2008 The Regents of the University of California. All
// Rights Reserved. Permission to use, copy, modify, and distribute this
// software and its documentation without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph appear in all copies.  This software program and
// documentation are copyrighted by The Regents of the University of
// California. The software program and documentation are supplied "AS
// IS", without any accompanying services from The Regents. The Regents
// does not warrant that the operation of the program will be
// uninterrupted or error-free. The end-user understands that the program
// was developed for research purposes and is advised not to rely
// exclusively on the program for any reason.  IN NO EVENT SHALL THE
// UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
// UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

package org.argouml.cognitive;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Hashtable;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;

import org.apache.log4j.Logger;

/**
 * Agency manages Critics.  Since classes are not really first class
 * objects in java, a singleton instance of Agency is made and passed
 * around as needed.  The Agency keeps a registry of all Critics that
 * should be applied to each type of design material. When a
 * design material instance is critiqued it asks Agency to apply all
 * registered Critic's.  In the current scheme there is a thread that
 * proactively, continuously critiques the Design at hand, even if
 * the user is idle! This is simple and it works.  The disadvantage
 * is that _all_ active critics related to a given design material are
 * applied, regardless of the reason for the critiquing and a lot of
 * CPU time is basically wasted.  <p>
 *
 * TODO: I am moving toward a more reactionary scheme in
 * which specific design manipulations in the editor cause critics
 * relevant to those manipulations to be applied.  This transition is
 * still half done.  Triggers are the critiquing requests.  The code
 * for triggers is currently dormant (latent?).<p>
 * 
 * TODO: There is a strong dependency cycle between Agency and Designer.  They
 * either need to be merged into a single class or partitioned differently, 
 * perhaps using an interface to break the cycle.  The Designer singleton gets
 * passed to almost every single part of the Critic subsystem, creating strong
 * coupling throughout. - tfm 20070620
 *
 * @author Jason Robbins
 */
public class Agency extends Observable { //implements java.io.Serialization
    /**
     * Logger.
     */
    private static final Logger LOG = Logger.getLogger(Agency.class);


    /**
     * A registry of all critics that are currently loaded into the
     * design environment.
     */
    private static Hashtable<Class, List<Critic>> criticRegistry = 
        new Hashtable<Class, List<Critic>>(100);
    
    private static List<Critic> critics = new ArrayList<Critic>();

    /**
     * The main control mechanism for determining which critics should
     * be active.
     */
    private ControlMech controlMech;
    
    private static Hashtable<String, Critic> singletonCritics = 
        new Hashtable<String, Critic>(40);


    /**
     * Construct a new Agency instance with the given ControlMech as the
     * main control mechanism for determining which critics should be
     * active.
     *
     * @param cm the given controlMech
     */
    public Agency(ControlMech cm) {
        controlMech = cm;
    }

    /**
     * Construct a new Agency instance and use a StandardCM as the main
     * control mechanism for determining which critics should be
     * active.
     */
    public Agency() {
        controlMech = new StandardCM();
    }

    /**
     * Since Java does not really support classes as first class
     * objects, there is one instance of Agency that is passed around as
     * needed.<p>
     *
     * theAgency is actually stored in <code>Designer.theDesigner()</code>.
     *
     * @see Designer#theDesigner
     * 
     * @return Agency the Agency instance
     */
    public static Agency theAgency() {
        Designer dsgr = Designer.theDesigner();
        if (dsgr == null) {
            return null;
	}
        return dsgr.getAgency();
    }

    /**
     * @return the registry.
     */
    private static Hashtable<Class, List<Critic>> getCriticRegistry() {
        return criticRegistry;
    }


    /**
     * @return the critics
     */
    public static List<Critic> getCriticList() {
        return critics;
    }

    /**
     * @param cr the critic to add/register
     */
    protected static void addCritic(Critic cr) {
        if (critics.contains(cr)) {
            return;
	}
        if (!(cr instanceof CompoundCritic)) {
            critics.add(cr);
	} else {
            for (Critic c : ((CompoundCritic) cr).getCriticList()) {
                addCritic(c);
            }
            return;
        }
    }

    /**
     * @param crClassName the critic class name
     * @param dmClassName the design material class name
     */
    public static void register(String crClassName, String dmClassName) {
        Class dmClass;
        try {
            dmClass = Class.forName(dmClassName);
        } catch (java.lang.ClassNotFoundException e) {
            LOG.error("Error loading dm " + dmClassName, e);
            return;
        }
        Critic cr = singletonCritics.get(crClassName);
        if (cr == null) {
            Class crClass;
            try {
                crClass = Class.forName(crClassName);
            } catch (java.lang.ClassNotFoundException e) {
                LOG.error("Error loading cr " + crClassName, e);
                return;
            }
            try {
                cr = (Critic) crClass.newInstance();
            } catch (java.lang.IllegalAccessException e) {
                LOG.error("Error instancating cr " + crClassName, e);
                return;
            } catch (java.lang.InstantiationException e) {
                LOG.error("Error instancating cr " + crClassName, e);
                return;
            }
            singletonCritics.put(crClassName, cr);
            addCritic(cr);
        }
        register(cr, dmClass);
    }

    /**
     * Register a critic in the global table of critics that have been
     * loaded. Critics are associated with one or more design material
     * classes. One way to do registration is in a static initializer of
     * the design material class. But additional (after-market) critics
     * could added through a menu command in some control panel...
     *
     * @param cr the critic to register
     * @param clazz the design material class that is to be criticized
     */
    public static void register(Critic cr, Class clazz) {
        List<Critic> theCritics = getCriticRegistry().get(clazz);
        if (theCritics == null) {
            theCritics = new ArrayList<Critic>();
            criticRegistry.put(clazz, theCritics);
        }
        if (!theCritics.contains(cr)) {
            theCritics.add(cr);
            notifyStaticObservers(cr);
            LOG.debug("Registered: " + theCritics.toString());
            cachedCritics.remove(clazz);
            addCritic(cr);
        }
    }

    /**
     * Transitional method for migration purposes.  Don't use!
     * @param cr the critic to register
     * @param clazz the UML class to be criticized
     */
    public static void register(Critic cr, Object clazz) {
        register(cr, (Class) clazz);
    }
    
    /**
     * Register a critic in the global table of critics that have been
     * loaded.
     * 
     * @param cr the critic to register
     */
    public static void register(Critic cr) {
        Set<Object> metas = cr.getCriticizedDesignMaterials();
        for (Object meta : metas) {
            register(cr, meta);            
        }
    }
    
    private static Hashtable<Class, Collection<Critic>> cachedCritics = 
        new Hashtable<Class, Collection<Critic>>();

    /**
     * Return a collection of all critics that can be applied to the
     * design material subclass, including inherited critics.
     *
     * @param clazz the design material to criticize
     * @return the collection of critics
     */
    public static Collection<Critic> criticsForClass(Class clazz) {
        Collection<Critic> col = cachedCritics.get(clazz);
        if (col == null) {
            col = new ArrayList<Critic>();
	    col.addAll(criticListForSpecificClass(clazz));
	    Collection<Class> classes = new ArrayList<Class>();
	    if (clazz.getSuperclass() != null) {
		classes.add(clazz.getSuperclass());
	    }
	    if (clazz.getInterfaces() != null) {
		classes.addAll(Arrays.asList(clazz.getInterfaces()));
	    }
            for (Class c : classes) {
		col.addAll(criticsForClass(c));
	    }
	    cachedCritics.put(clazz, col);
        }
        return col;

    }


    /**
     * Return the List of all critics that are directly
     * associated with the given design material subclass.<p>
     *
     * If there aren't any an empty List is returned.
     *
     * @param clazz the design material
     * @return the critics
     */
    protected static List<Critic> criticListForSpecificClass(Class clazz) {
        List<Critic> theCritics = getCriticRegistry().get(clazz);
        if (theCritics == null) {
            theCritics = new ArrayList<Critic>();
            criticRegistry.put(clazz, theCritics);
        }
        return theCritics;
    }


    /**
     * Apply all critics that can be applied to the given
     * design material instance as appropriate for the given
     * Designer. <p>
     *
     * I would call this critique, but it causes a compilation error
     * because it conflicts with the instance method critique!
     *
     * @param dm the design material
     * @param d the designer
     * @param reasonCode the reason
     */
    public static void applyAllCritics(
        Object dm,
        Designer d,
        long reasonCode) {
        Class dmClazz = dm.getClass();
        Collection<Critic> c = criticsForClass(dmClazz);
        applyCritics(dm, d, c, reasonCode);
    }

    /**
     * @param dm the design material
     * @param d the designer
     */
    public static void applyAllCritics(Object dm, Designer d) {
        Class dmClazz = dm.getClass();
        Collection<Critic> c = criticsForClass(dmClazz);
        applyCritics(dm, d, c, -1L);
    }

    /**
     * @param dm the design material
     * @param d the designer
     * @param theCritics the critics
     * @param reasonCode the reason
     */
    public static void applyCritics(
        Object dm,
        Designer d,
        Collection<Critic> theCritics,
        long reasonCode) {

        for (Critic c : theCritics) {
            if (c.isActive() && c.matchReason(reasonCode)) {
                try {
                    c.critique(dm, d);
                } catch (Exception ex) {
                    LOG.error("Disabling critique due to exception\n"
			      + c + "\n" + dm,
			      ex);
                    c.setEnabled(false);
                }
            }
        }
    }

    /**
     * Compute which critics should be active (i.e., they can be
     * applied by applyAllCritics) for a given Designer. <p>
     *
     * Note: I am setting global data, i.e. the
     * isEnabled bit in each critic, based on the needs of one designer.
     * I don't really support more than one Designer.
     * Which is why each designer 
     * (if we would support more than one designer) 
     * has his own Agency.
     *
     * TODO: should loop over simpler list of critics, not CompoundCritics
     *
     * @param d the designer
     */
    public void determineActiveCritics(Designer d) {
        for (Critic c : critics) {
            if (controlMech.isRelevant(c, d)) {
                c.beActive();
            } else {
                c.beInactive();
            }
        }
    }

    /**
     * Let some object receive notifications when the Agency changes
     * state.  Static observers are normal Observers on the singleton
     * instance of this class.
     *
     * @param obs the notified object
     */
    public static void addStaticObserver(Observer obs) {
        Agency a = theAgency();
        if (a == null) {
            return;
	}
        a.addObserver(obs);
    }

    /**
     * When the agency changes, notify observers.
     *
     * @param o the notified object
     */
    public static void notifyStaticObservers(Object o) {
        if (theAgency() != null) {
            theAgency().setChanged();
            theAgency().notifyObservers(o);
        }
    }

}
