/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: ProjSettingsNode.java
*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
*
* Electric(tm) is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.tool.user.projectSettings;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Set;
import java.util.Stack;
import java.util.TreeSet;
/**
* A basic class to hold information on project preferences.
* This node holds key-value pairs, who values are typically
* restricted to a few primitive types, and other ProjSettingNodes.
* <P>
* This class may be used as is, or may be extended to provide additional,
* more specific methods for setting/getting project preferences.
* Only settings accessible by this class' methods will be
* written to disk.
* Additionally, the extended class must be public, so that it can be
* created when the settings are read from a file.
* <P>
* Settings are written to the file in the order they are
* added to this class. This order is consistent and deterministic,
* and is not sorted after being added.
* <P>
* This class also retains the first setting for any key. This allows
* the user to get the "initial" or "factory" setting, which is always
* the first setting applied.
*/
class ProjSettingsNode implements Serializable {
// private final ProjSettingsNode parent;
// private final String key;
private final String path;
final LinkedHashMap<String, Object> data = new LinkedHashMap<String,Object>();
/**
* Create a new default proj settings node
*/
ProjSettingsNode() {
path = "";
}
/**
* Create a new default proj settings node
*/
private ProjSettingsNode(ProjSettingsNode parent, String key) {
// this.parent = parent;
// this.key = key;
path = parent + key + ".";
}
/**
* Returns a path to this ProjSettingsNode from the root.
* Keys in the path are separated by '.' char.
* @return path to this ProjSettingsNode from the root.
*/
public String getPath() {
return path;
}
@Override
public String toString() { return getPath(); }
/**
* Returns a set of keys, whose order is the
* order in which keys were added.
* @return a set of keys in deterministic order
*/
public Set<String> getKeys() {
return data.keySet();
}
// /**
// * Set the value for a key.
// * @param key a string key
// * @param setting a value
// */
// public void putValue(String key, Setting setting) {
// Object v = data.get(key);
// Object previousVal = null;
// if (v instanceof UninitializedPref) {
// // this overrides pref value, when setting was uninitialized so we couldn't set it before
// previousVal = ((UninitializedPref)v).value;
// }
// data.put(key, setting);
//
// if (previousVal != null && !equal(previousVal, setting)) {
// System.out.println("Warning: For key "+key+": project preferences value of "+previousVal+" overrides default of "+setting.getValue());
// setting.set(previousVal);
// }
// }
//
// public Setting getValue(String key) {
// Object obj = data.get(key);
// if (obj instanceof Setting)
// return (Setting)obj;
// if (obj == null) return null;
// //prIllegalRequestError(key);
// return null;
// }
//
// public void putNode(String key, ProjSettingsNode node) {
// data.put(key, node);
// }
public ProjSettingsNode getNode(String key) {
Object obj = data.get(key);
if (obj == null) {
obj = new ProjSettingsNode(this, key);
data.put(key, obj);
}
if (obj instanceof ProjSettingsNode)
return (ProjSettingsNode)obj;
//prIllegalRequestError(key);
return null;
}
// private void prIllegalRequestError(String key) {
// System.out.println("ERROR! Project Preferences key conflict: "+key);
// }
// ----------------------------- Protected --------------------------------
protected Object get(String key) {
return data.get(key);
}
protected void put(String key, Object node) {
data.put(key, node);
}
// ----------------------------- Utility ----------------------------------
public boolean equals(Object node) {
if (!(node instanceof ProjSettingsNode)) return false;
ProjSettingsNode otherNode = (ProjSettingsNode)node;
Set<String> myKeys = getKeys();
Set<String> otherKeys = otherNode.getKeys();
if (myKeys.size() != otherKeys.size()) return false;
for (String myKey : myKeys) {
if (!(otherKeys.contains(myKey))) return false;
Object myObj = get(myKey);
Object otherObj = otherNode.get(myKey);
if (myObj.getClass() != otherObj.getClass()) return false;
if (!myObj.equals(otherObj)) return false;
}
return true;
}
/**
* Print any differences between the two nodes
* @param node the nodes to compare
* @return true if differences found, false otherwise
*/
public boolean printDifferences(Object node) {
return printDifferences(node, new Stack<String>());
}
private boolean printDifferences(Object node, Stack<String> context) {
if (!(node instanceof ProjSettingsNode)) return true;
boolean differencesFound = false;
ProjSettingsNode otherNode = (ProjSettingsNode)node;
Set<String> myKeys = getKeys();
Set<String> otherKeys = otherNode.getKeys();
Set<String> allKeys = new TreeSet<String>();
allKeys.addAll(myKeys);
allKeys.addAll(otherKeys);
for (String key : allKeys) {
if (!myKeys.contains(key)) {
System.out.println("Warning: Key "+getKey(context, key)+" is missing from other settings");
differencesFound = true;
continue;
}
if (!otherKeys.contains(key)) {
System.out.println("Warning: Key "+getKey(context, key)+" is missing from current settings");
differencesFound = true;
continue;
}
Object myObj = get(key);
Object otherObj = otherNode.get(key);
if (myObj.getClass() != otherObj.getClass()) {
System.out.println("Warning: Value type mismatch for key "+getKey(context, key)+": "+
myObj.getClass().getName()+" vs "+otherObj.getClass().getName());
differencesFound = true;
continue;
}
if (myObj instanceof ProjSettingsNode) {
context.push(key);
if (((ProjSettingsNode)myObj).printDifferences(otherObj, context))
differencesFound = true;
context.pop();
} else if (!myObj.equals(otherObj)) {
System.out.println("Warning: Values not equal for key "+getKey(context, key)+": "+myObj+" vs "+otherObj);
differencesFound = true;
}
}
return differencesFound;
}
private String getKey(Stack<String> context, String key) {
return describeContext(context)+"."+key;
}
public static String describeContext(Stack<String> context) {
StringBuffer buf = new StringBuffer();
boolean first = true;
for (String name : context) {
if (first)
first = false;
else
buf.append(".");
buf.append(name);
}
if (buf.length() == 0) return "RootContext";
return buf.toString();
}
}