//----------------------------------------------------------------------------// // // // U n i t M a n a g e r // // // //----------------------------------------------------------------------------// // <editor-fold defaultstate="collapsed" desc="hdr"> // // Copyright © Hervé Bitteur and others 2000-2013. All rights reserved. // // This software is released under the GNU General Public License. // // Goto http://kenai.com/projects/audiveris to report bugs or suggestions. // //----------------------------------------------------------------------------// // </editor-fold> package omr.constant; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.jcip.annotations.ThreadSafe; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentSkipListSet; /** * Class {@code UnitManager} manages all units (aka classes), * for which we have a ConstantSet. * * <p>To help {@link UnitTreeTable} display the whole tree of UnitNodes, * UnitManager can pre-load all the classes known to contain a ConstantSet. * This list is kept up-to-date and stored as a property. * * @author Hervé Bitteur */ @ThreadSafe public class UnitManager { //~ Static fields/initializers --------------------------------------------- /** Name of this unit */ private static final String UNIT = UnitManager.class.getName(); /** The single instance of this class */ private static final UnitManager INSTANCE = new UnitManager(); /** Separator used in property that concatenates all unit names */ private static final String SEPARATOR = ";"; /** Usual logger utility */ private static final Logger logger = LoggerFactory.getLogger(UnitManager.class); //~ Instance fields -------------------------------------------------------- // /** The root node. */ private final PackageNode root = new PackageNode("<root>", null); /** Map of PackageNodes and UnitNodes. */ private final ConcurrentHashMap<String, Node> mapOfNodes = new ConcurrentHashMap<>(); /** Set of names of ConstantSets that still need to be initialized. */ private final ConcurrentSkipListSet<String> dirtySets = new ConcurrentSkipListSet<>(); /** * Lists of all units known as containing a constantset. * This is kept up-to-date and saved as a property. */ private Constant.String units; /** Flag to avoid storing units being pre-loaded. */ private volatile boolean storeIt = false; //~ Constructors ----------------------------------------------------------- //-------------// // UnitManager // //-------------// /** This is a singleton. */ private UnitManager () { mapOfNodes.put("<root>", root); } //~ Methods ---------------------------------------------------------------- //-------------// // getInstance // //-------------// /** * Report the single instance of this package. * * @return the single instance */ public static UnitManager getInstance () { return INSTANCE; } //--------// // addSet // //--------// /** * Add a ConstantSet, which means perhaps adding a UnitNode if not * already allocated and setting its ConstantSet reference to the * provided ConstantSet. * * @param set the ConstantSet to add to the hierarchy */ public void addSet (ConstantSet set) { ///log ("addSet set=" + set.getName()); retrieveUnit(set.getName()).setConstantSet(set); // Register this name in the dirty ones dirtySets.add(set.getName()); } //---------------// // checkAllUnits // //---------------// /** * Check if all defined constants are used by at least one unit. */ public void checkAllUnits () { SortedSet<String> constants = new TreeSet<>(); for (Node node : mapOfNodes.values()) { if (node instanceof UnitNode) { UnitNode unit = (UnitNode) node; ConstantSet set = unit.getConstantSet(); if (set != null) { for (int i = 0; i < set.size(); i++) { Constant constant = set.getConstant(i); constants.add( unit.getName() + "." + constant.getName()); } } } } dumpStrings("constants", constants); Collection<String> props = ConstantManager.getInstance(). getAllProperties(); props.removeAll(constants); dumpStrings("Non set-enclosed properties", props); dumpStrings( "Unused User properties", ConstantManager.getInstance().getUnusedUserProperties()); } //----------------// // checkDirtySets // //----------------// /** * Go through all registered to-be-initialized sets, and initialize * them, then clear the set of such dirty sets. */ public void checkDirtySets () { int rookies = 0; // We use (and clear) the collection of rookies for (Iterator<String> it = dirtySets.iterator(); it.hasNext();) { String name = it.next(); rookies++; // System.out.println( // Thread.currentThread().getName() + ": checkDirtySets. name=" + // name); UnitNode unit = (UnitNode) getNode(name); if (unit.getConstantSet().initialize()) { it.remove(); } } // System.out.println( // Thread.currentThread().getName() + ": checkDirtySets. rookies:" + // rookies); } //--------------// // dumpAllUnits // //--------------// /** * Dumps on the standard output the current value of all Constants * of all ConstantSets. */ public void dumpAllUnits () { StringBuilder sb = new StringBuilder(); sb.append(String.format("UnitManager. All Units:%n")); // Use alphabetical order for easier reading List<Node> nodes = new ArrayList<>(mapOfNodes.values()); Collections.sort(nodes, Node.nameComparator); for (Node node : nodes) { if (node instanceof UnitNode) { UnitNode unit = (UnitNode) node; // ConstantSet? ConstantSet set = unit.getConstantSet(); if (set != null) { sb.append(String.format("%n%s", set.dumpOf())); } } } logger.info(sb.toString()); } //---------// // getNode // //---------// /** * Retrieves a node object, knowing its path name. * * @param path fully qualified node name * @return the node object, or null if not found */ public Node getNode (String path) { return mapOfNodes.get(path); } //---------// // getRoot // //---------// /** * Return the PackageNode at the root of the node hierarchy. * * @return the root PackageNode */ public PackageNode getRoot () { return root; } //--------------// // preLoadUnits // //--------------// /** * Allows to pre-load the names of the various nodes in the * hierarchy, by simply extracting names stored at previous runs. * This will load the classes not already loaded. * This method is meant to be used by the UI which let the user browse and * modify the whole collection of constants. * * @param main the application main class name */ public void preLoadUnits (String main) { //log("pre-loading units"); String unitName; if (main != null) { unitName = main + ".Units"; } else { unitName = "Units"; } units = new Constant.String( UNIT, unitName, "", "List of units known as containing a ConstantSet"); // Initialize units using the constant 'units' final String[] tokens = units.getValue().split(SEPARATOR); storeIt = false; for (String unit : tokens) { try { ///System.out.println ("pre-loading '" + unit + "'..."); Class.forName(unit); // This loads its ConstantSet //log ("unit '" + unit + "' pre-loaded"); } catch (ClassNotFoundException ex) { System.err.println( "*** Cannot load ConstantSet " + unit + " " + ex); } } storeIt = true; // Save the latest set of Units storeUnits(); //log("all units have been pre-loaded from " + main); } //-------------// // searchUnits // //-------------// /** * Search for all the units for which the provided string is found * in the unit name or the unit description. * * @param string the string to search for * @return the set (perhaps empty) of the matching units, a mix of UnitNode * and Constant instances. */ public Set<Object> searchUnits (String string) { String str = string.toLowerCase(Locale.ENGLISH); Set<Object> found = new LinkedHashSet<>(); for (Node node : mapOfNodes.values()) { if (node instanceof UnitNode) { UnitNode unit = (UnitNode) node; // Search in unit name itself if (unit.getSimpleName().toLowerCase(Locale.ENGLISH).contains( str)) { found.add(unit); } // Search in unit constants, if any ConstantSet set = unit.getConstantSet(); if (set != null) { for (int i = 0; i < set.size(); i++) { Constant constant = set.getConstant(i); if (constant.getName().toLowerCase().contains(str) || constant.getDescription().toLowerCase(). contains(str)) { found.add(constant); } } } } } return found; } //---------// // addUnit // //---------// /** * Include a Unit in the hierarchy. * * @param unit the Unit to include */ private void addUnit (UnitNode unit) { //log ("addUnit unit=" + unit.getName()); // Update the hierarchy. Include it in the map, as well as all needed // intermediate package nodes if any is needed. String name = unit.getName(); // Add this node and its parents as needed if (mapOfNodes.putIfAbsent(name, unit) == null) { updateParents(unit); if (storeIt) { // Make this unit name permanent storeUnits(); } } } //-------------// // dumpStrings // //-------------// private void dumpStrings (String title, Collection<String> strings) { StringBuilder sb = new StringBuilder(); sb.append(String.format("%s:%n", title)); for (String string : strings) { sb.append(String.format("%s%n", string)); } logger.info(sb.toString()); } //--------------// // retrieveUnit // //--------------// /** * Looks for the unit with given name. * If the unit does not exist, it is created and inserted in the hierarchy. * * @param name the name of the desired unit * @return the unit (found, or created) */ private UnitNode retrieveUnit (String name) { Node node = getNode(name); if (node == null) { // Create a hosting unit node UnitNode unit = new UnitNode(name); addUnit(unit); return unit; } else if (node instanceof UnitNode) { return (UnitNode) node; } else if (node instanceof PackageNode) { logger.error("Unit with same name as package {}", name); } return null; } //------------// // storeUnits // //------------// /** * Build a string by concatenating all node names and store it to * disk for subsequent runs. */ private void storeUnits () { //log("storing units"); // Update the constant 'units' according to current units content StringBuilder buf = new StringBuilder(1024); for (String name : mapOfNodes.keySet()) { Node node = getNode(name); if (node instanceof UnitNode) { if (buf.length() > 0) { buf.append(SEPARATOR); } buf.append(name); } } // Side-effect: all constants are stored to disk units.setValue(buf.toString()); //log(units.getName() + "=" + units.getValue()); } //---------------// // updateParents // //---------------// /** * Update the chain of parents of a Unit, by walking up all the * package names found in the fully qualified Unit name, * creating PackageNodes when needed, or adding a new child to an * existing PackageNode. * * @param unit the Unit whose chain of parents is to be updated */ private void updateParents (UnitNode unit) { String name = unit.getName(); int length = name.length(); Node child = unit; for (int i = name.lastIndexOf('.', length - 1); i >= 0; i = name.lastIndexOf('.', i - 1)) { String parent = name.substring(0, i); Node obj = mapOfNodes.get(parent); // Create a provision node for a future parent. if (obj == null) { //log("No parent " + sub + " found. Creating PackageNode."); PackageNode pn = new PackageNode(parent, child); if (mapOfNodes.putIfAbsent(parent, pn) != null) { // Already done by someone else, give up return; } child = pn; } else if (obj instanceof PackageNode) { //log("PackageNode " + sub + " found. adding child"); ((PackageNode) obj).addChild(child); return; } else { Exception e = new IllegalStateException( "unexpected node type " + obj.getClass() + " in map."); e.printStackTrace(); return; } } // No intermediate parent found, so hook it to the root itself getRoot().addChild(child); } //---------------// // resetAllUnits // //---------------// /** * Reset all constants to their factory (source) value. */ public void resetAllUnits () { for (Node node : mapOfNodes.values()) { if (node instanceof UnitNode) { UnitNode unit = (UnitNode) node; ConstantSet set = unit.getConstantSet(); if (set != null) { for (int i = 0; i < set.size(); i++) { Constant constant = set.getConstant(i); constant.reset(); } } } } } }