diff --git a/openCherry/Bundles/org.opencherry.core.runtime/files.cmake b/openCherry/Bundles/org.opencherry.core.runtime/files.cmake index c2dca3dbac..14890e0f47 100644 --- a/openCherry/Bundles/org.opencherry.core.runtime/files.cmake +++ b/openCherry/Bundles/org.opencherry.core.runtime/files.cmake @@ -1,21 +1,26 @@ SET(SRC_CPP_FILES cherryIAdaptable.cpp cherryIAdapterManager.cpp + cherryIPreferencesService.cpp cherryPlatformObject.cpp cherryRuntime.cpp cherryRuntimePlugin.cpp + cherryBackingStoreException.cpp ) SET(INTERNAL_CPP_FILES - + cherryPreferencesService.cpp + cherryAbstractPreferencesStorage.cpp + cherryPreferences.cpp + cherryXMLPreferencesStorage.cpp ) SET(CPP_FILES manifest.cpp) foreach(file ${SRC_CPP_FILES}) SET(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) SET(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/openCherry/Bundles/org.opencherry.core.runtime/src/cherryBackingStoreException.cpp b/openCherry/Bundles/org.opencherry.core.runtime/src/cherryBackingStoreException.cpp new file mode 100644 index 0000000000..f585d97cdc --- /dev/null +++ b/openCherry/Bundles/org.opencherry.core.runtime/src/cherryBackingStoreException.cpp @@ -0,0 +1,9 @@ +#include "cherryBackingStoreException.h" + +#include + +namespace cherry { + + POCO_IMPLEMENT_EXCEPTION(BackingStoreException, Poco::RuntimeException, "BackingStore Exception") + +} \ No newline at end of file diff --git a/openCherry/Bundles/org.opencherry.core.runtime/src/cherryBackingStoreException.h b/openCherry/Bundles/org.opencherry.core.runtime/src/cherryBackingStoreException.h new file mode 100644 index 0000000000..19d130ba7e --- /dev/null +++ b/openCherry/Bundles/org.opencherry.core.runtime/src/cherryBackingStoreException.h @@ -0,0 +1,18 @@ +#ifndef CHERRYBACKINGSTOREEXCEPTION_H_ +#define CHERRYBACKINGSTOREEXCEPTION_H_ + +#include "cherryRuntimeDll.h" +#include + +namespace cherry +{ + /** + * Thrown to indicate that a preferences operation could not complete because of + * a failure in the backing store, or a failure to contact the backing store. + * + * @version $Revision: 1.11 $ + */ + POCO_DECLARE_EXCEPTION(CHERRY_RUNTIME, BackingStoreException, Poco::RuntimeException); +} + +#endif /* CHERRYBACKINGSTOREEXCEPTION_H_ */ \ No newline at end of file diff --git a/openCherry/Bundles/org.opencherry.core.runtime/src/cherryICherryPreferences.h b/openCherry/Bundles/org.opencherry.core.runtime/src/cherryICherryPreferences.h new file mode 100644 index 0000000000..6eefa4d38c --- /dev/null +++ b/openCherry/Bundles/org.opencherry.core.runtime/src/cherryICherryPreferences.h @@ -0,0 +1,29 @@ +#ifndef CHERRYICHERRYPREFERENCES_H_ +#define CHERRYICHERRYPREFERENCES_H_ + +#include "cherryRuntimeDll.h" +#include "cherryIPreferences.h" +#include "cherryMessage.h" + +#include +#include +#include + +namespace cherry +{ + + /// + /// Like IEclipsePreferences an extension to the osgi-IPreferences + /// to send out events when nodes or values changed in a node. + /// + struct CHERRY_RUNTIME ICherryPreferences : virtual public IPreferences + { + cherryInterfaceMacro(ICherryPreferences, cherry) + + + + }; + +} // namespace cherry + +#endif /*CHERRYICHERRYPREFERENCES_H_*/ diff --git a/openCherry/Bundles/org.opencherry.core.runtime/src/cherryICherryPreferencesService.h b/openCherry/Bundles/org.opencherry.core.runtime/src/cherryICherryPreferencesService.h new file mode 100644 index 0000000000..1aa78f0717 --- /dev/null +++ b/openCherry/Bundles/org.opencherry.core.runtime/src/cherryICherryPreferencesService.h @@ -0,0 +1,32 @@ +#ifndef CHERRYICHERRYPREFERENCESSERVICE_H_ +#define CHERRYICHERRYPREFERENCESSERVICE_H_ + +#include "cherryIPreferencesService.h" + +#include "Poco/File.h" + +#include +#include + +namespace cherry +{ + struct ICherryPreferencesService : public IPreferencesService + { + cherryInterfaceMacro(ICherryPreferencesService, cherry) + + /// + /// Try to import the prefs from the given file. + /// Existing properties will be overridden! + /// Returns true if the preferences could be imported. + /// + virtual void ImportPreferences(Poco::File f, std::string name="") = 0; + + /// + /// Exports the current system preferences to the given file. + /// + virtual void ExportPreferences(Poco::File f, std::string name="") = 0; + + }; +} // namespace cherry + +#endif /*CHERRYICHERRYPREFERENCESSERVICE_H_*/ diff --git a/openCherry/Bundles/org.opencherry.core.runtime/src/cherryIPreferences.h b/openCherry/Bundles/org.opencherry.core.runtime/src/cherryIPreferences.h new file mode 100644 index 0000000000..3280d5e2cc --- /dev/null +++ b/openCherry/Bundles/org.opencherry.core.runtime/src/cherryIPreferences.h @@ -0,0 +1,704 @@ +#ifndef CHERRYIPREFERENCES_H_ +#define CHERRYIPREFERENCES_H_ + +#include "cherryRuntimeDll.h" +#include "cherryObject.h" +#include "cherryBackingStoreException.h" + +#include +#include +#include + +namespace cherry +{ + + /** + * A node in a hierarchical collection of preference data. + * + *

+ * This interface allows applications to store and retrieve user and system + * preference data. This data is stored persistently in an + * implementation-dependent backing store. Typical implementations include flat + * files, OS-specific registries, directory servers and SQL databases. + * + *

+ * For each bundle, there is a separate tree of nodes for each user, and one for + * system preferences. The precise description of "user" and "system" will vary + * from one bundle to another. Typical information stored in the user preference + * tree might include font choice, and color choice for a bundle which interacts + * with the user via a servlet. Typical information stored in the system + * preference tree might include installation data, or things like high score + * information for a game program. + * + *

+ * Nodes in a preference tree are named in a similar fashion to directories in a + * hierarchical file system. Every node in a preference tree has a node name + * (which is not necessarily unique), a unique absolute path name , + * and a path name relative to each ancestor including itself. + * + *

+ * The root node has a node name of the empty std::string object (""). + * Every other node has an arbitrary node name, specified at the time it is + * created. The only restrictions on this name are that it cannot be the empty + * string, and it cannot contain the slash character ('/'). + * + *

+ * The root node has an absolute path name of "/". Children of the + * root node have absolute path names of "/" + <node name> + * . All other nodes have absolute path names of <parent's absolute + * path name> + "/" + <node name> . Note that + * all absolute path names begin with the slash character. + * + *

+ * A node n 's path name relative to its ancestor a is simply the + * string that must be appended to a 's absolute path name in order to + * form n 's absolute path name, with the initial slash character (if + * present) removed. Note that: + *

    + *
  • No relative path names begin with the slash character. + *
  • Every node's path name relative to itself is the empty string. + *
  • Every node's path name relative to its parent is its node name (except + * for the root node, which does not have a parent). + *
  • Every node's path name relative to the root is its absolute path name + * with the initial slash character removed. + *
+ * + *

+ * Note finally that: + *

    + *
  • No path name contains multiple consecutive slash characters. + *
  • No path name with the exception of the root's absolute path name end in + * the slash character. + *
  • Any string that conforms to these two rules is a valid path name. + *
+ * + *

+ * Each Preference node has zero or more properties associated with + * it, where a property consists of a name and a value. The bundle writer is + * free to choose any appropriate names for properties. Their values can be of + * type std::string,long,int,bool, + * std::vector,float, or double but they can + * always be accessed as if they were std::string objects. + * + *

+ * All node name and property name comparisons are case-sensitive. + * + *

+ * All of the methods that modify preference data are permitted to operate + * asynchronously; they may return immediately, and changes will eventually + * propagate to the persistent backing store, with an implementation-dependent + * delay. The flush method may be used to synchronously force updates + * to the backing store. + * + *

+ * Implementations must automatically attempt to flush to the backing store any + * pending updates for a bundle's preferences when the bundle is stopped or + * otherwise ungets the IPreferences Service. + * + *

+ * The methods in this class may be invoked concurrently by multiple threads in + * a single Java Virtual Machine (JVM) without the need for external + * synchronization, and the results will be equivalent to some serial execution. + * If this class is used concurrently by multiple JVMs that store their + * preference data in the same backing store, the data store will not be + * corrupted, but no other guarantees are made concerning the consistency of the + * preference data. + * + * + * @version $Revision: 1.10 $ + */ + struct CHERRY_RUNTIME IPreferences : virtual public Object + { + cherryInterfaceMacro(IPreferences, cherry) + + /** + * Associates the specified value with the specified key in this node. + * + * @param key key with which the specified value is to be associated. + * @param value value to be associated with the specified key. + * @throws NullPointerException if key or value is + * null. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method. + */ + virtual void Put(std::string key, std::string value) = 0; + + /** + * Returns the value associated with the specified key in this + * node. Returns the specified default if there is no value associated with + * the key, or the backing store is inaccessible. + * + * @param key key whose associated value is to be returned. + * @param def the value to be returned in the event that this node has no + * value associated with key or the backing store is + * inaccessible. + * @return the value associated with key, or def if + * no value is associated with key. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method. + * @throws NullPointerException if key is null. (A + * null default is permitted.) + */ + virtual std::string Get(std::string key, std::string def) const = 0; + + /** + * Removes the value associated with the specified key in this + * node, if any. + * + * @param key key whose mapping is to be removed from this node. + * @see #get(std::string,std::string) + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method. + */ + virtual void Remove(std::string key) = 0; + + /** + * Removes all of the properties (key-value associations) in this node. This + * call has no effect on any descendants of this node. + * + * @throws BackingStoreException if this operation cannot be completed due + * to a failure in the backing store, or inability to communicate + * with it. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method. + * @see #remove(std::string) + */ + virtual void Clear() throw(Poco::Exception, BackingStoreException) = 0; + + /** + * Associates a std::string object representing the specified + * int value with the specified key in this node. The + * associated string is the one that would be returned if the int + * value were passed to Integer.toString(int). This method is + * intended for use in conjunction with {@link #getInt}method. + * + *

+ * Implementor's note: it is not necessary that the property value + * be represented by a std::string object in the backing store. If the + * backing store supports integer values, it is not unreasonable to use + * them. This implementation detail is not visible through the + * IPreferences API, which allows the value to be read as an + * int (with getInt or a std::string (with + * get) type. + * + * @param key key with which the string form of value is to be associated. + * @param value value whose string form is to be associated with + * key. + * @throws NullPointerException if key is null. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method. + * @see #getInt(std::string,int) + */ + virtual void PutInt(std::string key, int value) = 0; + + /** + * Returns the int value represented by the std::string + * object associated with the specified key in this node. The + * std::string object is converted to an int as by + * Integer.parseInt(std::string). Returns the specified default if + * there is no value associated with the key, the backing store + * is inaccessible, or if Integer.parseInt(std::string) would throw a + * NumberFormatException if the associated value were + * passed. This method is intended for use in conjunction with the + * {@link #putInt}method. + * + * @param key key whose associated value is to be returned as an + * int. + * @param def the value to be returned in the event that this node has no + * value associated with key or the associated value + * cannot be interpreted as an int or the backing store is + * inaccessible. + * @return the int value represented by the std::string + * object associated with key in this node, or + * def if the associated value does not exist or cannot + * be interpreted as an int type. + * @throws NullPointerException if key is null. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method. + * @see #putInt(std::string,int) + * @see #get(std::string,std::string) + */ + virtual int GetInt(std::string key, int def) const = 0; + + /** + * Associates a std::string object representing the specified + * long value with the specified key in this node. The + * associated std::string object is the one that would be returned if + * the long value were passed to Long.toString(long). + * This method is intended for use in conjunction with the {@link #getLong} + * method. + * + *

+ * Implementor's note: it is not necessary that the value + * be represented by a std::string type in the backing store. If the + * backing store supports long values, it is not unreasonable to + * use them. This implementation detail is not visible through the + * IPreferences API, which allows the value to be read as a + * long (with getLong or a std::string (with + * get) type. + * + * @param key key with which the string form of value + * is to be associated. + * @param value value whose string form is to be associated with + * key. + * @throws NullPointerException if key is null. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method. + * @see #getLong(std::string,long) + */ + virtual void PutLong(std::string key, long value) = 0; + + /** + * Returns the long value represented by the std::string + * object associated with the specified key in this node. The + * std::string object is converted to a long as by + * Long.parseLong(std::string). Returns the specified default if + * there is no value associated with the key, the backing store + * is inaccessible, or if Long.parseLong(std::string) would throw a + * NumberFormatException if the associated value were + * passed. This method is intended for use in conjunction with the + * {@link #putLong}method. + * + * @param key key whose associated value is to be returned as a + * long value. + * @param def the value to be returned in the event that this node has no + * value associated with key or the associated value + * cannot be interpreted as a long type or the backing + * store is inaccessible. + * @return the long value represented by the std::string + * object associated with key in this node, or + * def if the associated value does not exist or cannot + * be interpreted as a long type. + * @throws NullPointerException if key is null. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method. + * @see #putLong(std::string,long) + * @see #get(std::string,std::string) + */ + virtual long GetLong(std::string key, long def) const = 0; + + /** + * Associates a std::string object representing the specified + * bool value with the specified key in this node. The + * associated string is "true" if the value is true, and "false" + * if it is false. This method is intended for use in + * conjunction with the {@link #getBool}method. + * + *

+ * Implementor's note: it is not necessary that the value be + * represented by a string in the backing store. If the backing store + * supports bool values, it is not unreasonable to use them. + * This implementation detail is not visible through the IPreferences + * API, which allows the value to be read as a bool + * (with getBool) or a std::string (with get) + * type. + * + * @param key key with which the string form of value is to be + * associated. + * @param value value whose string form is to be associated with + * key. + * @throws NullPointerException if key is null. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method. + * @see #getBool(std::string,bool) + * @see #get(std::string,std::string) + */ + virtual void PutBool(std::string key, bool value) = 0; + + /** + * Returns the bool value represented by the std::string + * object associated with the specified key in this node. Valid + * strings are "true", which represents true, and "false", which + * represents false. Case is ignored, so, for example, "TRUE" + * and "False" are also valid. This method is intended for use in + * conjunction with the {@link #putBool}method. + * + *

+ * Returns the specified default if there is no value associated with the + * key, the backing store is inaccessible, or if the associated + * value is something other than "true" or "false", ignoring case. + * + * @param key key whose associated value is to be returned as a + * bool. + * @param def the value to be returned in the event that this node has no + * value associated with key or the associated value + * cannot be interpreted as a bool or the backing store + * is inaccessible. + * @return the bool value represented by the std::string + * object associated with key in this node, or + * null if the associated value does not exist or cannot + * be interpreted as a bool. + * @throws NullPointerException if key is null. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method. + * @see #get(std::string,std::string) + * @see #putBool(std::string,bool) + */ + virtual bool GetBool(std::string key, bool def) const = 0; + + /** + * Associates a std::string object representing the specified + * float value with the specified key in this node. + * The associated std::string object is the one that would be returned + * if the float value were passed to + * Float.toString(float). This method is intended for use in + * conjunction with the {@link #getFloat}method. + * + *

+ * Implementor's note: it is not necessary that the value be + * represented by a string in the backing store. If the backing store + * supports float values, it is not unreasonable to use them. + * This implementation detail is not visible through the IPreferences + * API, which allows the value to be read as a float (with + * getFloat) or a std::string (with get) type. + * + * @param key key with which the string form of value is to be + * associated. + * @param value value whose string form is to be associated with + * key. + * @throws NullPointerException if key is null. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method. + * @see #getFloat(std::string,float) + */ + virtual void PutFloat(std::string key, float value) = 0; + + /** + * Returns the float value represented by the std::string + * object associated with the specified key in this node. The + * std::string object is converted to a float value as by + * Float.parseFloat(std::string). Returns the specified default if + * there is no value associated with the key, the backing store + * is inaccessible, or if Float.parseFloat(std::string) would throw a + * NumberFormatException if the associated value were passed. + * This method is intended for use in conjunction with the {@link #putFloat} + * method. + * + * @param key key whose associated value is to be returned as a + * float value. + * @param def the value to be returned in the event that this node has no + * value associated with key or the associated value + * cannot be interpreted as a float type or the backing + * store is inaccessible. + * @return the float value represented by the string associated + * with key in this node, or def if the + * associated value does not exist or cannot be interpreted as a + * float type. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method. + * @throws NullPointerException if key is null. + * @see #putFloat(std::string,float) + * @see #get(std::string,std::string) + */ + virtual float GetFloat(std::string key, float def) const = 0; + + /** + * Associates a std::string object representing the specified + * double value with the specified key in this node. + * The associated std::string object is the one that would be returned + * if the double value were passed to + * Double.toString(double). This method is intended for use in + * conjunction with the {@link #getDouble}method + * + *

+ * Implementor's note: it is not necessary that the value be + * represented by a string in the backing store. If the backing store + * supports double values, it is not unreasonable to use them. + * This implementation detail is not visible through the IPreferences + * API, which allows the value to be read as a double (with + * getDouble) or a std::string (with get) + * type. + * + * @param key key with which the string form of value is to be + * associated. + * @param value value whose string form is to be associated with + * key. + * @throws NullPointerException if key is null. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method. + * @see #getDouble(std::string,double) + */ + virtual void PutDouble(std::string key, double value) = 0; + + /** + * Returns the double value represented by the std::string + * object associated with the specified key in this node. The + * std::string object is converted to a double value as by + * Double.parseDouble(std::string). Returns the specified default if + * there is no value associated with the key, the backing store + * is inaccessible, or if Double.parseDouble(std::string) would throw + * a NumberFormatException if the associated value were passed. + * This method is intended for use in conjunction with the + * {@link #putDouble}method. + * + * @param key key whose associated value is to be returned as a + * double value. + * @param def the value to be returned in the event that this node has no + * value associated with key or the associated value + * cannot be interpreted as a double type or the backing + * store is inaccessible. + * @return the double value represented by the std::string + * object associated with key in this node, or + * def if the associated value does not exist or cannot + * be interpreted as a double type. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the the {@link #removeNode()}method. + * @throws NullPointerException if key is null. + * @see #putDouble(std::string,double) + * @see #get(std::string,std::string) + */ + virtual double GetDouble(std::string key, double def) const = 0; + + /** + * Associates a std::string object representing the specified + * std::vector with the specified key in this node. The + * associated std::string object the Base64 encoding of the + * std::vector, as defined in RFC 2045 , Section 6.8, + * with one minor change: the string will consist solely of characters from + * the Base64 Alphabet ; it will not contain any newline characters. + * This method is intended for use in conjunction with the + * {@link #getByteArray}method. + * + *

+ * Implementor's note: it is not necessary that the value be + * represented by a std::string type in the backing store. If the + * backing store supports std::vector values, it is not unreasonable + * to use them. This implementation detail is not visible through the + * IPreferences API, which allows the value to be read as an a + * std::vector object (with getByteArray) or a + * std::string object (with get). + * + * @param key key with which the string form of value + * is to be associated. + * @param value value whose string form is to be associated with + * key. + * @throws NullPointerException if key or value is + * null. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method. + * @see #getByteArray(std::string,std::vector) + * @see #get(std::string,std::string) + */ + virtual void PutByteArray(std::string key, std::string value) = 0; + + /** + * Returns the std::vector value represented by the std::string + * object associated with the specified key in this node. Valid + * std::string objects are Base64 encoded binary data, as + * defined in RFC 2045 , + * Section 6.8, with one minor change: the string must consist solely of + * characters from the Base64 Alphabet ; no newline characters or + * extraneous characters are permitted. This method is intended for use in + * conjunction with the {@link #putByteArray}method. + * + *

+ * Returns the specified default if there is no value associated with the + * key, the backing store is inaccessible, or if the associated + * value is not a valid Base64 encoded byte array (as defined above). + * + * @param key key whose associated value is to be returned as a + * std::vector object. + * @param def the value to be returned in the event that this node has no + * value associated with key or the associated value + * cannot be interpreted as a std::vector type, or the backing + * store is inaccessible. + * @return the std::vector value represented by the std::string + * object associated with key in this node, or + * def if the associated value does not exist or cannot + * be interpreted as a std::vector. + * @throws NullPointerException if key is null. (A + * null value for def is permitted.) + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method. + * @see #get(std::string,std::string) + * @see #putByteArray(std::string,std::vector) + */ + virtual std::string GetByteArray(std::string key, std::string def) const = 0; + + /** + * Returns all of the keys that have an associated value in this node. (The + * returned array will be of size zero if this node has no preferences and + * not null!) + * + * @return an array of the keys that have an associated value in this node. + * @throws BackingStoreException if this operation cannot be completed due + * to a failure in the backing store, or inability to communicate + * with it. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method. + */ + virtual std::vector Keys() const throw(Poco::Exception, BackingStoreException) = 0; + + /** + * Returns the names of the children of this node. (The returned array will + * be of size zero if this node has no children and not null!) + * + * @return the names of the children of this node. + * @throws BackingStoreException if this operation cannot be completed due + * to a failure in the backing store, or inability to communicate + * with it. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method. + */ + virtual std::vector ChildrenNames() const throw(Poco::Exception, BackingStoreException) = 0; + + /** + * Returns the parent of this node, or null if this is the root. + * + * @return the parent of this node. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method. + */ + virtual IPreferences::Pointer Parent() const = 0; + + /** + * Returns a named IPreferences object (node), creating it and any + * of its ancestors if they do not already exist. Accepts a relative or + * absolute pathname. Absolute pathnames (which begin with '/') + * are interpreted relative to the root of this node. Relative pathnames + * (which begin with any character other than '/') are + * interpreted relative to this node itself. The empty string ("") + * is a valid relative pathname, referring to this node itself. + * + *

+ * If the returned node did not exist prior to this call, this node and any + * ancestors that were created by this call are not guaranteed to become + * persistent until the flush method is called on the returned + * node (or one of its descendants). + * + * @param pathName the path name of the IPreferences object to + * return. + * @return the specified IPreferences object. + * @throws IllegalArgumentException if the path name is invalid. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method. + * @throws NullPointerException if path name is null. + * @see #flush() + */ + virtual IPreferences::Pointer Node(std::string pathName) = 0; + + /** + * Returns true if the named node exists. Accepts a relative or absolute + * pathname. Absolute pathnames (which begin with '/') are + * interpreted relative to the root of this node. Relative pathnames (which + * begin with any character other than '/') are interpreted + * relative to this node itself. The pathname "" is valid, and + * refers to this node itself. + * + *

+ * If this node (or an ancestor) has already been removed with the + * {@link #removeNode()}method, it is legal to invoke this method, + * but only with the pathname ""; the invocation will return + * false. Thus, the idiom p.nodeExists("") may be + * used to test whether p has been removed. + * + * @param pathName the path name of the node whose existence is to be + * checked. + * @return true if the specified node exists. + * @throws BackingStoreException if this operation cannot be completed due + * to a failure in the backing store, or inability to communicate + * with it. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method and + * pathname is not the empty string (""). + * @throws IllegalArgumentException if the path name is invalid (i.e., it + * contains multiple consecutive slash characters, or ends with a + * slash character and is more than one character long). + */ + virtual bool NodeExists(std::string pathName) const + throw(Poco::Exception, BackingStoreException) = 0; + + /** + * Removes this node and all of its descendants, invalidating any properties + * contained in the removed nodes. Once a node has been removed, attempting + * any method other than name(),absolutePath() or + * nodeExists("") on the corresponding IPreferences + * instance will fail with an IllegalStateException. (The + * methods defined on Object can still be invoked on a node after + * it has been removed; they will not throw IllegalStateException.) + * + *

+ * The removal is not guaranteed to be persistent until the flush + * method is called on the parent of this node. + * + * @throws IllegalStateException if this node (or an ancestor) has already + * been removed with the {@link #removeNode()}method. + * @throws BackingStoreException if this operation cannot be completed due + * to a failure in the backing store, or inability to communicate + * with it. + * @see #flush() + */ + virtual void RemoveNode() throw(Poco::Exception, BackingStoreException) = 0; + + /** + * Returns this node's name, relative to its parent. + * + * @return this node's name, relative to its parent. + */ + virtual std::string Name() const = 0; + + /** + * Returns this node's absolute path name. Note that: + *

    + *
  • Root node - The path name of the root node is "/". + *
  • Slash at end - Path names other than that of the root node may not + * end in slash ('/'). + *
  • Unusual names -"." and ".." have no + * special significance in path names. + *
  • Illegal names - The only illegal path names are those that contain + * multiple consecutive slashes, or that end in slash and are not the root. + *
+ * + * @return this node's absolute path name. + */ + virtual std::string AbsolutePath() const = 0; + + /** + * Forces any changes in the contents of this node and its descendants to + * the persistent store. + * + *

+ * Once this method returns successfully, it is safe to assume that all + * changes made in the subtree rooted at this node prior to the method + * invocation have become permanent. + * + *

+ * Implementations are free to flush changes into the persistent store at + * any time. They do not need to wait for this method to be called. + * + *

+ * When a flush occurs on a newly created node, it is made persistent, as + * are any ancestors (and descendants) that have yet to be made persistent. + * Note however that any properties value changes in ancestors are not + * guaranteed to be made persistent. + * + * @throws BackingStoreException if this operation cannot be completed due + * to a failure in the backing store, or inability to communicate + * with it. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method. + * @see #sync() + */ + virtual void Flush() throw(Poco::Exception, BackingStoreException) = 0; + + /** + * Ensures that future reads from this node and its descendants reflect any + * changes that were committed to the persistent store (from any VM) prior + * to the sync invocation. As a side-effect, forces any changes + * in the contents of this node and its descendants to the persistent store, + * as if the flush method had been invoked on this node. + * + * @throws BackingStoreException if this operation cannot be completed due + * to a failure in the backing store, or inability to communicate + * with it. + * @throws IllegalStateException if this node (or an ancestor) has been + * removed with the {@link #removeNode()}method. + * @see #flush() + */ + virtual void Sync() throw(Poco::Exception, BackingStoreException) = 0; + }; + +} // namespace cherry + +#endif /*CHERRYIPREFERENCES_H_*/ diff --git a/openCherry/Bundles/org.opencherry.core.runtime/src/cherryIPreferencesService.cpp b/openCherry/Bundles/org.opencherry.core.runtime/src/cherryIPreferencesService.cpp new file mode 100644 index 0000000000..8d80923145 --- /dev/null +++ b/openCherry/Bundles/org.opencherry.core.runtime/src/cherryIPreferencesService.cpp @@ -0,0 +1,8 @@ +#include "cherryIPreferencesService.h" + +namespace cherry +{ + + const std::string IPreferencesService::ID = "org.opencherry.core.runtime.preferences"; + +} diff --git a/openCherry/Bundles/org.opencherry.core.runtime/src/cherryIPreferencesService.h b/openCherry/Bundles/org.opencherry.core.runtime/src/cherryIPreferencesService.h new file mode 100644 index 0000000000..d406c4ad90 --- /dev/null +++ b/openCherry/Bundles/org.opencherry.core.runtime/src/cherryIPreferencesService.h @@ -0,0 +1,61 @@ +#ifndef CHERRYIPREFERENCESSERVICE_H_ +#define CHERRYIPREFERENCESSERVICE_H_ + +#include "cherryRuntimeDll.h" +#include "cherryService.h" +#include "cherryIPreferences.h" + +#include +#include + +namespace cherry +{ + + /** + * The Preferences Service. + * + *

+ * Each bundle using this service has its own set of preference trees: one for + * system preferences, and one for each user. + * + *

+ * A PreferencesService object is specific to the bundle which + * obtained it from the service registry. If a bundle wishes to allow another + * bundle to access its preferences, it should pass its + * PreferencesService object to that bundle. + * + */ + struct CHERRY_RUNTIME IPreferencesService : public Service + { + /// + /// A unique ID for the Service. + /// + static const std::string ID; + + cherryInterfaceMacro(IPreferencesService, cherry) + + /** + * Returns the root system node for the calling bundle. + * + * @return The root system node for the calling bundle. + */ + virtual IPreferences::Pointer GetSystemPreferences() = 0; + + /** + * Returns the root node for the specified user and the calling bundle. + * + * @param name The user for which to return the preference root node. + * @return The root node for the specified user and the calling bundle. + */ + virtual IPreferences::Pointer GetUserPreferences(std::string name) = 0; + + /** + * Returns the names of users for which node trees exist. + * + * @return The names of users for which node trees exist. + */ + virtual std::vector GetUsers() const = 0; + }; +} // namespace cherry + +#endif /*CHERRYIPREFERENCESSERVICE_H_*/ diff --git a/openCherry/Bundles/org.opencherry.core.runtime/src/cherryRuntimePlugin.cpp b/openCherry/Bundles/org.opencherry.core.runtime/src/cherryRuntimePlugin.cpp index fafff7ab15..c14d99956b 100644 --- a/openCherry/Bundles/org.opencherry.core.runtime/src/cherryRuntimePlugin.cpp +++ b/openCherry/Bundles/org.opencherry.core.runtime/src/cherryRuntimePlugin.cpp @@ -1,43 +1,46 @@ /*========================================================================= Program: openCherry Platform Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "cherryRuntimePlugin.h" #include "cherryPlatform.h" #include "service/cherryIExtensionPointService.h" #include "event/cherryPlatformEvents.h" +#include "internal/cherryPreferencesService.h" #include "Poco/Delegate.h" #include namespace cherry { const std::string RuntimePlugin::PLUGIN_ID = "org.opencherry.core.runtime"; void -RuntimePlugin::Start(IBundleContext::Pointer /*context*/) +RuntimePlugin::Start(IBundleContext::Pointer context) { CHERRY_INFO << "Runtime plugin activated!\n"; //Platform::GetEvents().platformStarted += // Poco::Delegate(this, &RuntimePlugin::onPlatformStarted); - + + PreferencesService::Pointer _PreferencesService(new PreferencesService()); + context->RegisterService(IPreferencesService::ID, _PreferencesService); } } diff --git a/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryAbstractPreferencesStorage.cpp b/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryAbstractPreferencesStorage.cpp new file mode 100644 index 0000000000..ed636a728b --- /dev/null +++ b/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryAbstractPreferencesStorage.cpp @@ -0,0 +1,34 @@ +#include "cherryAbstractPreferencesStorage.h" + +using namespace std; + +namespace cherry +{ + + AbstractPreferencesStorage::AbstractPreferencesStorage( const Poco::File& _File ) + : m_File(_File) + , m_Root(0) + { + + } + + AbstractPreferencesStorage::~AbstractPreferencesStorage() + { + + } + + IPreferences::Pointer AbstractPreferencesStorage::GetRoot() const + { + return m_Root; + } + + const Poco::File& AbstractPreferencesStorage::GetFile() const + { + return m_File; + } + + void AbstractPreferencesStorage::SetFile( const Poco::File& f ) + { + m_File = f; + } +} diff --git a/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryAbstractPreferencesStorage.h b/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryAbstractPreferencesStorage.h new file mode 100644 index 0000000000..a400bf9b47 --- /dev/null +++ b/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryAbstractPreferencesStorage.h @@ -0,0 +1,64 @@ +#ifndef CHERRYABSTRACTPREFERENCESSTORAGE_H_ +#define CHERRYABSTRACTPREFERENCESSTORAGE_H_ + +#include "../cherryRuntimeDll.h" +#include "cherryIPreferences.h" + +#include "Poco/File.h" + +namespace cherry +{ + + /// + /// Interface to flush Preferences. + /// + class CHERRY_RUNTIME AbstractPreferencesStorage : public Object + { + + public: + cherryObjectMacro(cherry::AbstractPreferencesStorage) + + /// + /// Saves the path, sets the root initially to 0. + /// In subclasses try to read data from file here. + /// + AbstractPreferencesStorage(const Poco::File& _File); + + /// + /// Pure virtual (abstract class) + /// + virtual ~AbstractPreferencesStorage(); + + /// + /// Flushes the given (or all) prefs persistently + /// + virtual void Flush(IPreferences* prefs) throw(Poco::Exception, BackingStoreException) = 0; + + /// + /// Returns the root prefs + /// + virtual IPreferences::Pointer GetRoot() const; + + /// + /// Returns the path of the file + /// + virtual const Poco::File& GetFile() const; + + /// + /// Sets the file + /// + virtual void SetFile(const Poco::File& f); + + protected: + + /// + /// Path to the file where the data is stored + /// + Poco::File m_File; + /// + /// Pointer to the root Preferences + /// + IPreferences::Pointer m_Root; + }; +} +#endif /* CHERRYABSTRACTPREFERENCESSTORAGE_H_ */ diff --git a/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryPreferences.cpp b/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryPreferences.cpp new file mode 100644 index 0000000000..015675e590 --- /dev/null +++ b/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryPreferences.cpp @@ -0,0 +1,602 @@ +#include "cherryPreferences.h" +#include "cherryAbstractPreferencesStorage.h" +#include "Poco/ScopedLock.h" +#include + +using namespace std; + +namespace cherry +{ + + Preferences::Preferences(const PropertyMap& _Properties + , const std::string& _Name + , Preferences* _Parent + , AbstractPreferencesStorage* _Storage) + : m_Properties(_Properties) + , m_Name(_Name) + , m_Parent(_Parent) + , m_Removed(false) + , m_Storage(_Storage) + { + // root node if parent is 0 + if (_Parent != 0) + { + m_Path = _Parent->AbsolutePath() + (_Parent->AbsolutePath() == "/" ? "": "/") + _Name; + // save as child in parent + _Parent->m_Children.push_back(Preferences::Pointer(this)); + m_Root = _Parent->m_Root; + } + else + { + // root path + m_Path = "/"; + m_Root = this; + } + + } + + bool Preferences::Has( string key ) const + { + Poco::ScopedLock scopedMutex(m_Mutex); + return (m_Properties.find(key) != m_Properties.end()); + } + + bool Preferences::IsDirty() const + { + Poco::ScopedLock scopedMutex(m_Mutex); + + bool dirty = m_Dirty; + for (ChildrenList::const_iterator it = m_Children.begin() + ; it != m_Children.end(); ++it) + { + // break condition: if one node is dirty the whole tree is dirty + if(dirty) + break; + else + dirty = (*it)->IsDirty(); + } + + return dirty; + } + + string Preferences::ToString() const + { + Poco::ScopedLock scopedMutex(m_Mutex); + ostringstream s; + s << "Preferences[" << m_Path << "]"; + return s.str(); + } + + bool Preferences::Equals(const Preferences* rhs) const + { + Poco::ScopedLock scopedMutex(m_Mutex); + if(rhs == 0) + return false; + + return (this->m_Path == rhs->m_Path); + } + + Preferences::PropertyMap Preferences::GetProperties() const + { + return m_Properties; + } + + Preferences::ChildrenList Preferences::GetChildren() const + { + return m_Children; + } + + string Preferences::AbsolutePath() const + { + Poco::ScopedLock scopedMutex(m_Mutex); + return m_Path; + } + + vector Preferences::ChildrenNames() const throw(Poco::Exception, BackingStoreException) + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + vector names; + for (ChildrenList::const_iterator it = m_Children.begin() + ; it != m_Children.end(); ++it) + { + names.push_back((*it)->Name()); + } + return names; + } + + + AbstractPreferencesStorage* Preferences::GetStorage() const + { + return m_Storage; + } + + void Preferences::Clear() + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + m_Properties.clear(); + this->SetDirty(true); + } + + void Preferences::Flush() throw(Poco::Exception, BackingStoreException) + { + Poco::ScopedLock scopedMutex(m_Mutex); + // ensure that this is the parent + + m_Storage->Flush(this); + // if something is written, make the tree undirty + this->SetDirty(false); + } + + string Preferences::Get(string key, string def) const + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + return this->Has(key)? m_Properties.find(key)->second: def; + } + + bool Preferences::GetBool(string key, bool def) const + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + return this->Has(key)? (m_Properties.find(key)->second == "true"? true: false): def; + } + + string Preferences::GetByteArray(string key, string def) const + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + return this->Has(key)? Base64::decode(m_Properties.find(key)->second): def; + } + + double Preferences::GetDouble(string key, double def) const + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + return this->Has(key)? atof(m_Properties.find(key)->second.c_str()): def; + } + + float Preferences::GetFloat(string key, float def) const + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + return this->Has(key)? static_cast(atof(m_Properties.find(key)->second.c_str())): def; + } + + int Preferences::GetInt(string key, int def) const + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + return this->Has(key)? atoi(m_Properties.find(key)->second.c_str()): def; + } + + long Preferences::GetLong(string key, long def) const + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + return this->Has(key)? atol(m_Properties.find(key)->second.c_str()): def; + } + + vector Preferences::Keys() const throw(Poco::Exception, BackingStoreException) + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + + vector keys; + for (PropertyMap::const_iterator it = m_Properties.begin() + ; it != m_Properties.end(); it++) + { + keys.push_back(it->first); + } + return keys; + } + + string Preferences::Name() const + { + Poco::ScopedLock scopedMutex(m_Mutex); + return m_Name; + } + + IPreferences::Pointer Preferences::Node(string pathName) + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + AssertPath(pathName); + + IPreferences::Pointer node; + + // absolute path + if(pathName[0] == '/') + { + pathName = pathName.substr(1); + // call root with this relative path + return m_Root->Node(pathName); + } + // relative path + else + { + // check if pathName contains anymore names + string name = pathName; + + // create new child nodes as long as there are names in the path + string::size_type pos = pathName.find("/"); + // cut from the beginning + if(pos != string::npos) + { + name = pathName.substr(0, pos); + pathName = pathName.substr(pos+1); + } + + // now check if node exists->if not: make new + for (ChildrenList::iterator it = m_Children.begin() + ; it != m_Children.end(); it++) + { + // node found + if((*it)->Name() == name && (*it)->IsRemoved() == false) + { + node = IPreferences::Pointer((*it).GetPointer()); + break; + } + + } + + // node not found create new one + if(node.IsNull()) + { + // the new node automatically pushes itself into the children array of *this* + Preferences::Pointer newNode(new Preferences(PropertyMap(), name, this, m_Storage)); + node = newNode.GetPointer(); + // this branch is dirty now -> prefs must be rewritten persistently + this->SetDirty(true); + + } + + // call Node() again if there are any names left on the path + if(pos != string::npos) + node = node->Node(pathName); + } + + return node; + } + + bool Preferences::NodeExists(string pathName) const + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + AssertPath(pathName); + + bool nodeExists = false; + + // absolute path + if(pathName[0] == '/') + { + pathName = pathName.substr(1); + // call root with this relative path + return m_Root->NodeExists(pathName); + } + // relative path + else + { + // check if pathName contains anymore names + string name = pathName; + + // create new child nodes as long as there are names in the path + string::size_type pos = pathName.find_first_of("/"); + // cut from the beginning + if(pos != string::npos) + { + name = pathName.substr(0, pos); + pathName = pathName.substr(pos+1); + } + + // now check if node exists->if not: make new + for (ChildrenList::const_iterator it = m_Children.begin() + ; it != m_Children.end(); it++) + { + // node found + if((*it)->Name() == name) + { + // call recursively if more names on the path exist + if(pos != string::npos) + nodeExists = (*it)->NodeExists(pathName); + else + nodeExists = true; + break; + } + } + } + + return nodeExists; + } + + void Preferences::Put(string key, string value) + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + + m_Properties[key] = value; + this->SetDirty(true); + } + + void Preferences::PutByteArray(string key, string value) + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + this->Put(key, Base64::encode(value)); + } + + void Preferences::PutBool(string key, bool value) + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + this->Put(key, value ? "true" : "false"); + } + + void Preferences::PutDouble(string key, double value) + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + this->Put(key, Preferences::ToString(value)); + } + + void Preferences::PutFloat(string key, float value) + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + this->Put(key, Preferences::ToString(value)); + } + + void Preferences::PutInt(string key, int value) + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + this->Put(key, Preferences::ToString(value)); + } + + void Preferences::PutLong(string key, long value) + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + this->Put(key, Preferences::ToString(value)); + } + + void Preferences::Remove(string key) + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + PropertyMap::iterator it = m_Properties.find(key); + if(it != m_Properties.end()) + m_Properties.erase(it); + } + + void Preferences::RemoveNode() + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + this->SetRemoved(true); + m_Parent->m_Children.erase(std::find(m_Parent->m_Children.begin(), m_Parent->m_Children.end(), + Preferences::Pointer(this))); + } + + void Preferences::Sync() throw(Poco::Exception, BackingStoreException) + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + this->Flush(); + } + + void Preferences::AssertValid() const + { + Poco::ScopedLock scopedMutex(m_Mutex); + if(m_Removed) + { + ostringstream s; s << "no node at '" << m_Path << "'"; + throw Poco::IllegalStateException(s.str()); + } + } + + void Preferences::AssertPath(string pathName) + { + if(pathName.find("//") != string::npos) + { + ostringstream s; s << "Illegal // in m_Path m_Name '" << pathName << "'"; + throw invalid_argument(s.str()); + } + int strLength = pathName.length(); + if(pathName.length() > 1 && pathName[strLength-1] == '/') + { + ostringstream s; s << "Trailing / in m_Path m_Name '" << pathName << "'"; + throw invalid_argument(s.str()); + } + } + + IPreferences::Pointer Preferences::Parent() const + { + Poco::ScopedLock scopedMutex(m_Mutex); + AssertValid(); + return IPreferences::Pointer(m_Parent); + } + + void Preferences::SetDirty( bool _Dirty ) + { + Poco::ScopedLock scopedMutex(m_Mutex); + m_Dirty = _Dirty; + +/* + for (ChildrenList::iterator it = m_Children.begin() + ; it != m_Children.end(); ++it) + { + (*it)->SetDirty(_Dirty); + } +*/ + + } + + void Preferences::SetRemoved( bool _Removed ) + { + Poco::ScopedLock scopedMutex(m_Mutex); + m_Removed = _Removed; + + for (ChildrenList::iterator it = m_Children.begin() + ; it != m_Children.end(); ++it) + { + (*it)->SetRemoved(_Removed); + } + + } + +/* + Preferences::ChildrenList& Preferences::GetChildren() const + { + return m_Children; + }*/ + + + bool Preferences::IsRemoved() const + { + Poco::ScopedLock scopedMutex(m_Mutex); + return m_Removed; + } + + Preferences::~Preferences() + { + } +} + +namespace Base64 +{ + string Base64::encode(const string &sString) + { + static const string base64Table( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + ); + static const char fillChar = '='; + string::size_type length = sString.length(); + string result; + + // Allocate memory for the converted string + result.reserve(length * 8 / 6 + 1); + + for(string::size_type nPos = 0; nPos < length; nPos++) { + char code; + + // Encode the first 6 bits + code = (sString[nPos] >> 2) & 0x3f; + result.append(1, base64Table[code]); + + // Encode the remaining 2 bits with the next 4 bits (if present) + code = (sString[nPos] << 4) & 0x3f; + if(++nPos < length) + code |= (sString[nPos] >> 4) & 0x0f; + result.append(1, base64Table[code]); + + if(nPos < length) { + code = (sString[nPos] << 2) & 0x3f; + if(++nPos < length) + code |= (sString[nPos] >> 6) & 0x03; + + result.append(1, base64Table[code]); + } else { + ++nPos; + result.append(1, fillChar); + } + + if(nPos < length) { + code = sString[nPos] & 0x3f; + result.append(1, base64Table[code]); + } else { + result.append(1, fillChar); + } + } + + return result; + } + + string Base64::decode(const string &sString) + { + static const string::size_type np = string::npos; + + static const string::size_type DecodeTable[] = + { + // 0 1 2 3 4 5 6 7 8 9 + np, np, np, np, np, np, np, np, np, np, // 0 - 9 + np, np, np, np, np, np, np, np, np, np, // 10 - 19 + np, np, np, np, np, np, np, np, np, np, // 20 - 29 + np, np, np, np, np, np, np, np, np, np, // 30 - 39 + np, np, np, 62, np, np, np, 63, 52, 53, // 40 - 49 + 54, 55, 56, 57, 58, 59, 60, 61, np, np, // 50 - 59 + np, np, np, np, np, 0, 1, 2, 3, 4, // 60 - 69 + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 70 - 79 + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 80 - 89 + 25, np, np, np, np, np, np, 26, 27, 28, // 90 - 99 + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // 100 - 109 + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, // 110 - 119 + 49, 50, 51, np, np, np, np, np, np, np, // 120 - 129 + np, np, np, np, np, np, np, np, np, np, // 130 - 139 + np, np, np, np, np, np, np, np, np, np, // 140 - 149 + np, np, np, np, np, np, np, np, np, np, // 150 - 159 + np, np, np, np, np, np, np, np, np, np, // 160 - 169 + np, np, np, np, np, np, np, np, np, np, // 170 - 179 + np, np, np, np, np, np, np, np, np, np, // 180 - 189 + np, np, np, np, np, np, np, np, np, np, // 190 - 199 + np, np, np, np, np, np, np, np, np, np, // 200 - 209 + np, np, np, np, np, np, np, np, np, np, // 210 - 219 + np, np, np, np, np, np, np, np, np, np, // 220 - 229 + np, np, np, np, np, np, np, np, np, np, // 230 - 239 + np, np, np, np, np, np, np, np, np, np, // 240 - 249 + np, np, np, np, np, np // 250 - 256 + }; + static const char fillChar = '='; + + string::size_type length = sString.length(); + string result; + + result.reserve(length); + + for(string::size_type nPos = 0; nPos < length; nPos++) { + unsigned char c, c1; + + c = (char) DecodeTable[(unsigned char)sString[nPos]]; + nPos++; + c1 = (char) DecodeTable[(unsigned char)sString[nPos]]; + c = (c << 2) | ((c1 >> 4) & 0x3); + result.append(1, c); + + if(++nPos < length) { + c = sString[nPos]; + if(fillChar == c) + break; + + c = (char) DecodeTable[(unsigned char)sString[nPos]]; + c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf); + result.append(1, c1); + } + + if(++nPos < length) { + c1 = sString[nPos]; + if(fillChar == c1) + break; + + c1 = (char) DecodeTable[(unsigned char)sString[nPos]]; + c = ((c << 6) & 0xc0) | c1; + result.append(1, c); + } + } + + return result; + } + +} + +ostream& operator<<( ostream& os,const cherry::Preferences& m ) +{ + os << m.ToString(); + return os; +} + +ostream& operator<<( ostream& os,const cherry::Preferences* m ) +{ + os << m->ToString(); + return os; +} diff --git a/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryPreferences.h b/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryPreferences.h new file mode 100644 index 0000000000..87b393d969 --- /dev/null +++ b/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryPreferences.h @@ -0,0 +1,324 @@ +#ifndef CHERRYPREFERENCES_H_ +#define CHERRYPREFERENCES_H_ + +#include "../cherryRuntimeDll.h" +#include "cherryIPreferences.h" + +#include +#include +#include +#include +#include "Poco/Mutex.h" + +namespace cherry +{ + class AbstractPreferencesStorage; + /// + /// Implementation of the OSGI Preferences Interface. + /// Wraps a DOMNode. + /// + class CHERRY_RUNTIME Preferences: public IPreferences + { + + public: + /// + /// For use with cherry::SmartPtr + /// + cherryObjectMacro(cherry::Preferences) + /// + /// Maps a string key to a string value + /// + typedef std::map PropertyMap; + /// + /// The list of Child nodes + /// + typedef std::vector ChildrenList; + + /// + /// Constructs a new preference node. + /// \param _Properties the key->value pairs of this preference node + /// \param _Path the absolute path to this node, e.g. "/general/editors/font" + /// \param _Name the name of this node, e.g. "font" + /// \param _FileName the absolute path to the file in which this preferences tree will be saved to + /// \param _Parent the parent node or 0 if this is the root + /// \param _Root the root of this preference tree + /// + Preferences(const PropertyMap& _Properties + , const std::string& _Name + , Preferences* _Parent + , AbstractPreferencesStorage* _Storage); + + /// + /// Nothing to do here + /// + virtual ~Preferences(); + + /// + /// Prints out the absolute path of the preference node. + /// + std::string ToString() const; + /// + /// Returns if this node and his silblings have to be rewritten persistently + /// + bool IsDirty() const; + /// + /// Returns if this node is removed + /// + bool IsRemoved() const; + /// + /// Returns if this node has property with a specific key + /// + bool Has(std::string key) const; + /// + /// Returns true if the absolute paths are the same + /// + bool Equals(const Preferences* rhs) const; + /// + /// Returns all Properties as map. + /// + PropertyMap GetProperties() const; + /// + /// Returns a reference to the children list in order to add or remove nodes. + /// *ATTENTION*: Should only be used + /// when constructing the preferences tree from a persistent location. Normally, one would + /// only use the IPreferences methods + /// + ChildrenList GetChildren() const; + + //# Begin of IPreferences methods + + /// + /// \see IPreferences::AbsolutePath() + /// + virtual std::string AbsolutePath() const; + + /// + /// \see IPreferences::ChildrenNames() + /// + virtual std::vector ChildrenNames() const throw(Poco::Exception, BackingStoreException); + + /// + /// \see IPreferences::ChildrenNames() + /// + virtual AbstractPreferencesStorage* GetStorage() const; + + /// + /// \see IPreferences::Clear() + /// + virtual void Clear(); + + /// + /// \see IPreferences::Flush() + /// + virtual void Flush() throw(Poco::Exception, BackingStoreException); + + /// + /// \see IPreferences::Get() + /// + virtual std::string Get(std::string key, std::string def) const; + + /// + /// \see IPreferences::GetBool() + /// + virtual bool GetBool(std::string key, bool def) const; + + /// + /// \see IPreferences::GetByteArray() + /// + virtual std::string GetByteArray(std::string key, std::string def) const; + + /// + /// \see IPreferences::GetDouble() + /// + virtual double GetDouble(std::string key, double def) const; + + /// + /// \see IPreferences::GetFloat() + /// + virtual float GetFloat(std::string key, float def) const; + + /// + /// \see IPreferences::GetInt() + /// + virtual int GetInt(std::string key, int def) const; + + /// + /// \see IPreferences::GetLong() + /// + virtual long GetLong(std::string key, long def) const; + + /// + /// \see IPreferences::Keys() + /// + std::vector Keys() const throw(Poco::Exception, BackingStoreException); + + /// + /// \see IPreferences::Name() + /// + virtual std::string Name() const; + + /// + /// \see IPreferences::Node() + /// + virtual IPreferences::Pointer Node(std::string pathName); + + /// + /// \see IPreferences::NodeExists() + /// + virtual bool NodeExists(std::string pathName) const; + + /// + /// \see IPreferences::Parent() + /// + virtual IPreferences::Pointer Parent() const; + + /// + /// \see IPreferences::Put() + /// + virtual void Put(std::string key, std::string value); + + /// + /// \see IPreferences::PutByteArray() + /// + virtual void PutByteArray(std::string key, std::string value); + + /// + /// \see IPreferences::PutBool() + /// + virtual void PutBool(std::string key, bool value); + + /// + /// \see IPreferences::PutDouble() + /// + virtual void PutDouble(std::string key, double value); + + /// + /// \see IPreferences::Sync() + /// + virtual void PutFloat(std::string key, float value); + + /// + /// \see IPreferences::PutInt() + /// + virtual void PutInt(std::string key, int value); + + /// + /// \see IPreferences::PutLong() + /// + virtual void PutLong(std::string key, long value); + + /// + /// \see IPreferences::Remove() + /// + virtual void Remove(std::string key); + + /// + /// \see IPreferences::RemoveNode() + /// + virtual void RemoveNode(); + + /// + /// \see IPreferences::Sync() + /// + virtual void Sync() throw(Poco::Exception, BackingStoreException); + + //# End of IPreferences methods + + protected: + + /// + /// Checks if this node is about to be removed. + /// \throws IllegalStateException + /// + void AssertValid() const; + /// + /// Checks a path value for validity. + /// \throws invalid_argument + /// + static void AssertPath(std::string pathName); + /// + /// Converts any value to a string (using stream operator "<<") + /// + template + static std::string ToString(const T& obj, int precision = 12 ) + { + std::ostringstream s; s.precision(precision); s << obj; return s.str(); + } + /// + /// Sets the dirty flag recursively on all child nodes. + /// + void SetDirty(bool _Dirty); + /// + /// Sets the removed flag recursively on all child nodes. + /// + void SetRemoved(bool _Removed); + + protected: + /// + /// Holds all Key/Value Pairs. + /// + std::map m_Properties; + /// + /// Holds all child nodes (explicit ownership). + /// + std::vector m_Children; + /// + /// Saves the absolute path of this node (calculated in the constructor) + /// + std::string m_Path; + /// + /// Saves the name of this node (set when read from backend) + /// + std::string m_Name; + /// + /// Saves the parent of this node + /// + Preferences* m_Parent; + /// + /// Saves the root of this tree + /// + Preferences* m_Root; + /// + /// Saves if something changed on this branch. + /// Meaning that you would have to rewrite it. + /// + bool m_Dirty; + /// + /// Saves if this Node is removed (will not be saved to the backend). + /// + bool m_Removed; + /// + /// A storage to call the flush method. + /// + AbstractPreferencesStorage* m_Storage; + /// + /// A mutex to avoid concurrency crashes. Mutable because we need to use Mutex::lock() in const functions + /// + mutable Poco::Mutex m_Mutex; + }; + +} + +namespace Base64 +{ + /// + /// Encode string to base64 (needed for writing byte arrays) + /// + std::string encode(const std::string &sString); + /// + /// Decode base64 to string (needed for reading byte arrays) + /// + std::string decode(const std::string &sString); +}; + +/// +/// Uses Preferences::ToString to print node information +/// +std::ostream& operator<<(std::ostream& os,const cherry::Preferences& m); + +/// +/// Uses Preferences::ToString to print node information +/// +std::ostream& operator<<(std::ostream& os,const cherry::Preferences* m); + +#endif /* CHERRYPREFERENCES_H_ */ diff --git a/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryPreferencesService.cpp b/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryPreferencesService.cpp new file mode 100644 index 0000000000..58c4a50de8 --- /dev/null +++ b/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryPreferencesService.cpp @@ -0,0 +1,158 @@ +#include "cherryPreferencesService.h" +#include "cherryXMLPreferencesStorage.h" + +#include "Poco/ScopedLock.h" +#include "Poco/DirectoryIterator.h" + +using namespace std; + +bool cherry::PreferencesService::IsA( const std::type_info& type ) const +{ + std::string name(GetType().name()); + return name == type.name() || Service::IsA(type); +} + +const std::type_info& cherry::PreferencesService::GetType() const +{ + return typeid(cherry::IPreferencesService); +} + +std::string cherry::PreferencesService::GetDefaultPreferencesDirPath() +{ + string _PreferencesDirPath; + _PreferencesDirPath = Poco::Path::home() + ".BlueBerryPrefs"; + return _PreferencesDirPath; +} + + +std::string cherry::PreferencesService::GetDefaultPreferencesFileName() +{ + return "prefs.xml"; +} + +cherry::PreferencesService::PreferencesService(string _PreferencesDir) +: m_PreferencesDir(_PreferencesDir) +{ + if(m_PreferencesDir.empty()) + m_PreferencesDir = GetDefaultPreferencesDirPath(); + + Poco::File prefDir(m_PreferencesDir); + if(!prefDir.exists()) + prefDir.createDirectory(); + + Poco::DirectoryIterator dirIt(prefDir); + + Poco::File f; + + while(dirIt.path().getFileName() != "") + { + f = dirIt.path(); + + if(f.isFile()) + { + // check if this file is a preferences file + string::size_type pos = dirIt.name().rfind(GetDefaultPreferencesFileName()); + if(pos != string::npos) + { + string userName = dirIt.name().substr(0, pos); + // set the storage to 0 (will be loaded later) + m_PreferencesStorages[userName] = AbstractPreferencesStorage::Pointer(0); + } + } + ++dirIt; + } +} + +cherry::PreferencesService::~PreferencesService() +{ + // flush all preferences + for (map::const_iterator it = m_PreferencesStorages.begin() + ; it != m_PreferencesStorages.end(); ++it) + { + // the preferences storage may be 0 if the corresponding file was never loaded + if(it->second != 0) + it->second->GetRoot()->Flush(); + } + +} + +cherry::IPreferences::Pointer cherry::PreferencesService::GetSystemPreferences() +{ + Poco::ScopedLock scopedMutex(m_Mutex); + // sys prefs are indicated by an empty user string + return this->GetUserPreferences(""); +} + +cherry::IPreferences::Pointer cherry::PreferencesService::GetUserPreferences( std::string name ) +{ + Poco::ScopedLock scopedMutex(m_Mutex); + IPreferences::Pointer userPrefs(0); + + map::iterator it + = m_PreferencesStorages.find(name); + + // does not exist or is not loaded yet + if(it == m_PreferencesStorages.end() || it->second.IsNull()) + { + std::string path = m_PreferencesDir; + + if(name.empty()) + path = path + Poco::Path::separator() + GetDefaultPreferencesFileName(); + // + else + path = path + Poco::Path::separator() + name + GetDefaultPreferencesFileName(); + + XMLPreferencesStorage::Pointer storage(new XMLPreferencesStorage(path)); + m_PreferencesStorages[name] = storage; + } + + userPrefs = m_PreferencesStorages[name]->GetRoot(); + + return userPrefs; +} + +std::vector cherry::PreferencesService::GetUsers() const +{ + Poco::ScopedLock scopedMutex(m_Mutex); + vector users; + + for (map::const_iterator it = m_PreferencesStorages.begin() + ; it != m_PreferencesStorages.end(); ++it) + { + users.push_back(it->first); + } + + return users; +} + +void cherry::PreferencesService::ImportPreferences( Poco::File f, std::string name ) +{ + map::iterator it + = m_PreferencesStorages.find(name); + + if(it->second == 0) + { + this->GetUserPreferences(name); + } + + Poco::File defaultFile = it->second->GetFile(); + XMLPreferencesStorage::Pointer storage(new XMLPreferencesStorage(f)); + m_PreferencesStorages[name] = storage; + storage->SetFile(defaultFile); +} + +void cherry::PreferencesService::ExportPreferences( Poco::File f, std::string name ) +{ + map::iterator it + = m_PreferencesStorages.find(name); + + if(it->second == 0) + { + this->GetUserPreferences(name); + } + Poco::File temp = it->second->GetFile(); + it->second->SetFile(f); + it->second->GetRoot()->Flush(); + it->second->SetFile(temp); + +} \ No newline at end of file diff --git a/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryPreferencesService.h b/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryPreferencesService.h new file mode 100644 index 0000000000..64d0199835 --- /dev/null +++ b/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryPreferencesService.h @@ -0,0 +1,90 @@ +#ifndef CHERRYPREFERENCESSERVICE_H_ +#define CHERRYPREFERENCESSERVICE_H_ + +#include "cherryRuntimeDll.h" +//#include "cherryService.h" +#include "cherryICherryPreferencesService.h" +#include "cherryAbstractPreferencesStorage.h" + +#include +#include + +namespace cherry +{ + /** + * Implementation of the IPreferencesService Interface + */ + class CHERRY_RUNTIME PreferencesService : public ICherryPreferencesService + { + public: + cherryObjectMacro(PreferencesService) + + //# From cherry::Service + virtual bool IsA(const std::type_info& type) const; + virtual const std::type_info& GetType() const; + + /// + /// Returns the default name for the preferences data file + /// + static std::string GetDefaultPreferencesFileName(); + + /// + /// Returns the path to the directory where all preference data is stored. + /// + static std::string GetDefaultPreferencesDirPath(); + + /// + /// Reads in all users for which preferences exist. + /// + PreferencesService(std::string _PreferencesDir=""); + + /// + /// Nothing to do here so far. + /// + virtual ~PreferencesService(); + + /** + * If no system preference file exists create a new AbstractPreferencesStorage. + * \see IPreferencesService::GetSystemPreferences() + */ + virtual IPreferences::Pointer GetSystemPreferences(); + + /** + * If no user preference file exists create a new AbstractPreferencesStorage. + * \see IPreferencesService::GetUserPreferences() + */ + virtual IPreferences::Pointer GetUserPreferences(std::string name); + + /** + * \see IPreferencesService::GetUsers() + */ + virtual std::vector GetUsers() const; + + + /// + /// \see IPreferencesService::ImportPreferences() + /// + virtual void ImportPreferences(Poco::File f, std::string name=""); + + /// + /// \see IPreferencesService::ExportPreferences() + /// + virtual void ExportPreferences(Poco::File f, std::string name=""); + protected: + /// + /// Holds the directory where the preferences files will be stored + /// + std::string m_PreferencesDir; + /// + /// Maps all user names to their preference storage. + /// + std::map m_PreferencesStorages; + /// + /// A mutex to avoid concurrency crashes. Mutable because we need to use Mutex::lock() in const functions + /// + mutable Poco::Mutex m_Mutex; + + }; +} // namespace cherry + +#endif /*CHERRYPREFERENCESSERVICE_H_*/ diff --git a/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryXMLPreferencesStorage.cpp b/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryXMLPreferencesStorage.cpp new file mode 100644 index 0000000000..1b0e68a05f --- /dev/null +++ b/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryXMLPreferencesStorage.cpp @@ -0,0 +1,161 @@ +#include "cherryXMLPreferencesStorage.h" +#include "cherryPreferences.h" + +#include "mbilog.h" +#define WARNMSG LOG_WARN("XMLPreferencesStorage") + +#include "Poco/File.h" +#include "Poco/DOM/DOMParser.h" +#include "Poco/DOM/DOMWriter.h" +#include "Poco/DOM/Element.h" +#include "Poco/XML/XMLWriter.h" +#include "Poco/DOM/Document.h" +#include "Poco/DOM/NodeIterator.h" +#include "Poco/DOM/NodeFilter.h" +#include "Poco/DOM/AutoPtr.h" +#include "Poco/SAX/InputSource.h" +#include "Poco/DOM/NamedNodeMap.h" +#include "Poco/DOM/NodeList.h" +#include "Poco/DOM/Element.h" +#include "Poco/Exception.h" + +using Poco::XML::DOMParser; +using Poco::XML::InputSource; +using Poco::XML::Document; +using Poco::XML::NodeIterator; +using Poco::XML::NodeFilter; +using Poco::XML::Node; +using Poco::XML::Element; +using Poco::XML::AutoPtr; +using Poco::Exception; + +#include +#include + +namespace cherry +{ + + + XMLPreferencesStorage::XMLPreferencesStorage( const Poco::File& _File ) + : AbstractPreferencesStorage(_File) + { + // file already exists + if(_File.exists()) + { + // build preferences tree from file + InputSource src(_File.path()); + DOMParser parser; + AutoPtr pDoc = parser.parse(&src); + this->ToPreferencesTree(pDoc->documentElement(), 0); + } + + // if root is null make new one + if(this->m_Root.IsNull()) + { + this->m_Root = Preferences::Pointer(new Preferences(Preferences::PropertyMap(), "", 0, this)); + } + } + + + + void XMLPreferencesStorage::Flush(IPreferences* _Preferences) throw(Poco::Exception, BackingStoreException) + { + try + { + this->ToDOMTree(dynamic_cast(this->m_Root.GetPointer()), 0); + // flush dom to file + Poco::XML::DOMWriter writer; + //writer.setNewLine("\n"); + writer.setOptions(Poco::XML::XMLWriter::PRETTY_PRINT); + std::ofstream f; + f.open (this->m_File.path().c_str()); + + // PRECISION setting + // f.precision(10); + writer.writeNode(f, m_Document); + + f.close(); + } + catch (std::exception& e) + { + WARNMSG << e.what(); + } + } + + XMLPreferencesStorage::~XMLPreferencesStorage() + { + + } + + void XMLPreferencesStorage::ToPreferencesTree( Poco::XML::Node* DOMNode, Preferences* prefParentNode ) + { + // collect properties from attributes + Preferences::PropertyMap properties; + Poco::XML::Element* elem = dynamic_cast(DOMNode); + std::string name = elem->getAttribute("name"); + + // create a new Preferences node + Preferences::Pointer newNode(new Preferences(properties, name, prefParentNode, this)); + // save the new Preferences node as root node if needed + if(prefParentNode == 0) + m_Root = newNode; + + // recursion: now go on for all child nodes of DOMNode + Poco::XML::NodeList* childNodes = elem->childNodes(); + Poco::XML::Node* currentNode = 0; + Poco::XML::Element* prop = 0; + std::string key; + std::string value; + + for (unsigned int i = 0; i != childNodes->length(); ++i) + { + currentNode = childNodes->item(i); + + if(currentNode->nodeName() == "preferences") + this->ToPreferencesTree(currentNode, newNode.GetPointer()); + else if(currentNode->nodeName() == "property") + { + prop = dynamic_cast(currentNode); + key = prop->getAttribute("name"); + value = prop->getAttribute("value"); + newNode->Put(key, value); + } + } + } + + void XMLPreferencesStorage::ToDOMTree( Preferences* prefNode, Poco::XML::Node* parentDOMNode ) + { + if(parentDOMNode == 0) + { + m_Document = new Poco::XML::Document; + parentDOMNode = m_Document; + } + + //# create DOMNode from Preferences + Element* newNode = m_Document->createElement("preferences"); + newNode->setAttribute("name", prefNode->Name()); + + // make attributes + Preferences::PropertyMap properties = prefNode->GetProperties(); + Element* newProp = 0; + for (Preferences::PropertyMap::iterator it = properties.begin() + ; it != properties.end(); it++) + { + Element* newProp = m_Document->createElement("property"); + newProp->setAttribute("name", it->first); + newProp->setAttribute("value", it->second); + newNode->appendChild(newProp); + } + // save in parentDOMNode + parentDOMNode->appendChild(newNode); + + // recursion: now do that for all child Preferences nodes + Preferences::ChildrenList children = prefNode->GetChildren(); + for (Preferences::ChildrenList::iterator it = children.begin() + ; it != children.end(); it++) + { + this->ToDOMTree((*it).GetPointer(), newNode); + } + + } +} diff --git a/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryXMLPreferencesStorage.h b/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryXMLPreferencesStorage.h new file mode 100644 index 0000000000..cd4ee5bf27 --- /dev/null +++ b/openCherry/Bundles/org.opencherry.core.runtime/src/internal/cherryXMLPreferencesStorage.h @@ -0,0 +1,57 @@ +#ifndef CHERRYXMLPREFERENCES_H_ +#define CHERRYXMLPREFERENCES_H_ + +#include "../cherryRuntimeDll.h" +#include "cherryAbstractPreferencesStorage.h" + +#include "Poco/Path.h" +#include "Poco/DOM/Node.h" +#include "Poco/DOM/Document.h" +#include "Poco/AutoPtr.h" + +namespace cherry +{ + class Preferences; + /// + /// Implementation of the OSGI Preferences Interface. + /// Wraps a DOMNode. + /// + class CHERRY_RUNTIME XMLPreferencesStorage: public AbstractPreferencesStorage + { + public: + /// + /// For use with cherry::SmartPtr + /// + cherryObjectMacro(cherry::XMLPreferencesStorage) + /// + /// Construct a new XML-based PreferencesStorage + /// + XMLPreferencesStorage(const Poco::File& _File); + + /// + /// To be implemented in the subclasses. + /// + virtual void Flush(IPreferences* _Preferences) throw(Poco::Exception, BackingStoreException); + + /// + /// Nothing to do here + /// + virtual ~XMLPreferencesStorage(); + + protected: + /// + /// Construct Preferences tree from DOM tree (recursive) + /// Construct a PreferencesNode from the DOMNode by using prefParentNode + /// + void ToPreferencesTree(Poco::XML::Node* DOMNode, Preferences* prefParentNode); + /// + /// Construct DOM tree from Preferences tree (recursive) + /// Construct a DOMNode from the prefNode by using parentDOMNode + /// + void ToDOMTree(Preferences* prefNode, Poco::XML::Node* parentDOMNode); + protected: + Poco::AutoPtr m_Document; + + }; +} +#endif /* CHERRYXMLPREFERENCES_H_ */ diff --git a/openCherry/Bundles/org.opencherry.ui.qt/src/cherryIQtPreferencePage.h b/openCherry/Bundles/org.opencherry.ui.qt/src/cherryIQtPreferencePage.h new file mode 100644 index 0000000000..a31a286279 --- /dev/null +++ b/openCherry/Bundles/org.opencherry.ui.qt/src/cherryIQtPreferencePage.h @@ -0,0 +1,56 @@ +/*========================================================================= + +Program: openCherry Platform +Language: C++ +Date: $Date: 2009-01-23 09:44:29 +0100 (Fr, 23 Jan 2009) $ +Version: $Revision: 16084 $ + +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + +#ifndef CHERRYIQTPREFERENCEPAGE_H_ +#define CHERRYIQTPREFERENCEPAGE_H_ + +#include + +#include "cherryIPreferencePage.h" + +namespace cherry +{ + +/** + * \ingroup org_opencherry_ui + * + */ +struct IQtPreferencePage : public QWidget, public IPreferencePage +{ + + cherryInterfaceMacro(IQtPreferencePage, cherry); + +public: + IQtPreferencePage(QWidget * parent = 0, Qt::WindowFlags f = 0) + : QWidget(parent, f) + { + } + + /// + /// Invoked when the OK button was clicked in the preferences dialog + /// + virtual bool PerformOk() = 0; + + /// + /// Invoked when the Cancel button was clicked in the preferences dialog + /// + virtual void PerformCancel() = 0; +}; + +} + +#endif /*CHERRYIQTPREFERENCEPAGE_H_*/ diff --git a/openCherry/Bundles/org.opencherry.ui/plugin.xml b/openCherry/Bundles/org.opencherry.ui/plugin.xml index 7482297dac..6a488fda46 100644 --- a/openCherry/Bundles/org.opencherry.ui/plugin.xml +++ b/openCherry/Bundles/org.opencherry.ui/plugin.xml @@ -1,48 +1,50 @@ + + \ No newline at end of file diff --git a/openCherry/Bundles/org.opencherry.ui/schema/keywords.exsd b/openCherry/Bundles/org.opencherry.ui/schema/keywords.exsd new file mode 100644 index 0000000000..f1dde3a660 --- /dev/null +++ b/openCherry/Bundles/org.opencherry.ui/schema/keywords.exsd @@ -0,0 +1,136 @@ + + + + + + + + + The keywords extension point defines keywords and a unique id for reference by other schemas. + +See propertyPages and preferencePages. + + + + + + + + + + + + + + + + a fully qualified identifier of the target extension point + + + + + + + an optional identifier of the extension instance + + + + + + + an optional name of the extension instance + + + + + + + + + + + + + + + + + + + The id is the unique id used to reference the keyword. + + + + + + + The human readable label of the keyword + + + + + + + + + + + + + + + 3.1 + + + + + + + + + The following is an example of a keyword extension: +<p> +<pre> + <extension + point="org.eclipse.ui.keywords"> + <keyword + label="presentation tab themes" + id="com.xyz.AppearanceKeywords"/> + </extension> +</pre> +</p> + + + + + + + + + There currently is no public API for retrieving or creating keywords other than this extension point. + + + + + + + + + Keywords are used only with preference and property pages. See the <samp>keywordReference</samp> element of the org.eclipse.ui.propertyPages and org.eclipse.ui.preferencePages extension points. + + + + + + + + + Copyright (c) 2005,2006 IBM Corporation and others.<br> +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 <a +href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> + + + + diff --git a/openCherry/Bundles/org.opencherry.ui/schema/preferencePages.exsd b/openCherry/Bundles/org.opencherry.ui/schema/preferencePages.exsd new file mode 100644 index 0000000000..0603381f34 --- /dev/null +++ b/openCherry/Bundles/org.opencherry.ui/schema/preferencePages.exsd @@ -0,0 +1,195 @@ + + + + + + + + + The workbench provides one common dialog box for preferences. +The purpose of this extension point is to allow plug-ins to add +pages to the preference dialog box. When preference dialog box +is opened (initiated from the menu bar), pages contributed in +this way will be added to the dialog box. + +<p> +The preference dialog +box provides for hierarchical grouping of the pages. For this +reason, a page can optionally specify a <samp>category</samp> attribute. +This +attribute represents a path composed of parent page IDs separated +by '/'. If this attribute is omitted or if any of the parent +nodes in the path cannot be found, the page will be added at +the root level. +</p> + + + + + + + + + + + + a fully qualified identifier of the target extension point + + + + + + + an optional identifier of the extension instance + + + + + + + an optional name of the extension instance + + + + + + + + + + + + + + + + + + + + + + + a unique name that will be used to identify this page. + + + + + + + a translatable name that will be used in the UI for this page. + + + + + + + + + + a name of the fully qualified class that implements +<samp>org.eclipse.ui.IWorkbenchPreferencePage</samp>. + + + + + + + + + + a path indicating the location of the page in the preference tree. The path may either be a parent node ID or a sequence + of IDs separated by '/', representing the full path from the root node. + + + + + + + + + + + + + A reference by a preference page to a keyword. See the keywords extension point. + + + + + + + The id of the keyword being referred to. + + + + + + + + + + + + + + + The following is an example for the preference extension point: +<p> +<pre> + <extension + point="org.eclipse.ui.preferencePages"> + <page + id="com.xyz.prefpage1" + name="XYZ" + class="com.xyz.prefpages.PrefPage1"> + <keywordReference id="xyz.Keyword"/> + </page> + <page + id="com.xyz.prefpage2" + name="Keyboard Settings" + class="com.xyz.prefpages.PrefPage2" + category="com.xyz.prefpage1"> + </page> + </extension> +</pre> +</p> + + + + + + + + + The value of the attribute class must represent a fully qualified name of the class that implements +<samp>org.eclipse.ui.IWorkbenchPreferencePage</samp>. + + + + + + + + + + + The workbench adds several pages for setting the preferences of the platform. Pages registered +through this extension will be added after them according to their category information. + + + + + + + + + Copyright (c) 2002, 2005 IBM Corporation and others.<br> +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 <a +href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> + + + + diff --git a/openCherry/Bundles/org.opencherry.ui/src/cherryIPreferencePage.h b/openCherry/Bundles/org.opencherry.ui/src/cherryIPreferencePage.h new file mode 100644 index 0000000000..41856a3b95 --- /dev/null +++ b/openCherry/Bundles/org.opencherry.ui/src/cherryIPreferencePage.h @@ -0,0 +1,49 @@ +/*========================================================================= + +Program: openCherry Platform +Language: C++ +Date: $Date: 2009-01-23 09:44:29 +0100 (Fr, 23 Jan 2009) $ +Version: $Revision: 16084 $ + +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + +#ifndef CHERRYIPREFERENCEPAGE_H_ +#define CHERRYIPREFERENCEPAGE_H_ + +#include "cherryObject.h" +#include "cherryIPreferences.h" + +namespace cherry +{ + +/** + * \ingroup org_opencherry_ui + * + */ +struct IPreferencePage : virtual public Object +{ + + cherryInterfaceMacro(IPreferencePage, cherry); + + /// + /// Invoked when the OK button was clicked in the preferences dialog + /// + virtual bool PerformOk() = 0; + + /// + /// Invoked when the Cancel button was clicked in the preferences dialog + /// + virtual void PerformCancel() = 0; +}; + +} + +#endif /*CHERRYIPREFERENCEPAGE_H_*/ diff --git a/openCherry/Bundles/org.opencherry.ui/src/cherryXMLMemento.cpp b/openCherry/Bundles/org.opencherry.ui/src/cherryXMLMemento.cpp index 064f8338b1..921f22655a 100644 --- a/openCherry/Bundles/org.opencherry.ui/src/cherryXMLMemento.cpp +++ b/openCherry/Bundles/org.opencherry.ui/src/cherryXMLMemento.cpp @@ -1,370 +1,371 @@ /*========================================================================= Program: openCherry Platform Language: C++ Date: $Date: 2008-10-01 11:54:41 +0200 (Mi, 01 Okt 2008) $ Version: $Revision: 15350 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "cherryXMLMemento.h" #include "Poco/DOM/NodeList.h" #include "Poco/XML/NamePool.h" #include "Poco/DOM/NamedNodeMap.h" #include "Poco/DOM/Text.h" #include "Poco/DOM/Attr.h" #include "Poco/DOM/DOMWriter.h" #include "Poco/DOM/DOMParser.h" #include "Poco/DOM/DOMBuilder.h" #include "Poco/SAX/InputSource.h" #include const std::string TAG_ID = "IMemento.internal.id"; cherry::XMLMemento::XMLMemento(Poco::XML::Document* document, Poco::XML::Element* elem) { factory = document; element = elem; } cherry::XMLMemento::Pointer cherry::XMLMemento::CreateReadRoot(cherry::XMLMemento::XMLByteInputStream& reader) throw(WorkbenchException) { return CreateReadRoot(reader, ""); } cherry::XMLMemento::Pointer cherry::XMLMemento::CreateReadRoot(cherry::XMLMemento::XMLByteInputStream& reader, const std::string& baseDir) throw(WorkbenchException) { //TODO try catch blog Poco::XML::NamePool* namePool = new Poco::XML::NamePool(); Poco::XML::Document* doc = new Poco::XML::Document(namePool); Poco::XML::DOMParser* parser = new Poco::XML::DOMParser(); Poco::XML::InputSource* source = new Poco::XML::InputSource(reader); source->setSystemId(baseDir); doc = parser->parse(source); Poco::XML::Element* elem = doc->documentElement(); //TODO really documentElement? return cherry::XMLMemento::New(doc, elem); } cherry::XMLMemento::Pointer cherry::XMLMemento::CreateWriteRoot(const std::string& type) { // TODO // try{ Poco::XML::NamePool* namePool = new Poco::XML::NamePool(); Poco::XML::Document* doc = new Poco::XML::Document(namePool); Poco::XML::Element* elem = doc->createElement(type); doc->appendChild(elem); return cherry::XMLMemento::New(doc, elem); //}catch() //TODO: look for poco exceptions //{ //} } cherry::IMemento::Pointer cherry::XMLMemento::CreateChild(const std::string& type) { Poco::XML::Element* child = factory->createElement(type); element->appendChild(child); return XMLMemento::New(factory, child); } cherry::IMemento::Pointer cherry::XMLMemento::CreateChild(const std::string& type, const std::string& id) { Poco::XML::Element* child = factory->createElement(type); child->setAttribute(TAG_ID, id); //$NON-NLS-1$ element->appendChild(child); return XMLMemento::New(factory, child); } cherry::IMemento::Pointer cherry::XMLMemento::CopyChild(IMemento::Pointer child) { //TODO check any casting errors Poco::XML::Element* elem = child.Cast()->GetElement(); Poco::XML::Element* newElement = dynamic_cast(factory->importNode(elem,true)); element->appendChild(newElement); return XMLMemento::New(factory, newElement); } cherry::IMemento::Pointer cherry::XMLMemento::GetChild(const std::string& type) const { // Get the nodes. cherry::XMLMemento::Pointer memento; Poco::XML::Element* child = element->getChildElement(type); // Find the first node which is a child of this node if (child) { memento = cherry::XMLMemento::New(factory, child); return memento; } // A child was not found. return memento; } std::vector< cherry::IMemento::Pointer > cherry::XMLMemento::GetChildren(const std::string& type) const { std::vector< IMemento::Pointer > mementos; Poco::XML::NodeList* elementList = element->getElementsByTagName(type); mementos.resize(elementList->length()); for (unsigned long i = 0; i < elementList->length(); i++) { Poco::XML::Element* elem = dynamic_cast(elementList->item(i)); mementos[i] = cherry::XMLMemento::New(factory, elem); } return mementos; } float cherry::XMLMemento::GetFloat(const std::string& key) const { //TODO: check typeconversion and make error handling! std::stringstream ss; float val=0; const std::string& attr = element->getAttribute(key); // attr >> ss >> val; return val; } const std::string& cherry::XMLMemento::GetType() const { return element->nodeName(); } const std::string& cherry::XMLMemento::GetID() const { //TODO: make error handling! return element->getAttribute(TAG_ID); } int cherry::XMLMemento::GetInteger(const std::string& key) const { std::stringstream ss; int val=0; std::string attr = element->getAttribute(key); ss << attr; ss >> val; return val; } const std::string& cherry::XMLMemento::GetString(const std::string& key) const { return element->getAttribute(key); } bool cherry::XMLMemento::GetBoolean(const std::string& key) const { //TODO: what if attr contains something else then "false"? std::string attr = element->getAttribute(key); if (attr == "true") return true; else return false; } const std::string& cherry::XMLMemento::GetTextData() const { Poco::XML::Text* textNode = GetTextNode(); if (textNode != NULL) { return textNode->getData(); } return ""; //TODO check if NULL or better "" } std::vector< std::string > cherry::XMLMemento::GetAttributeKeys() const { std::vector < std::string > values; Poco::XML::NamedNodeMap* nnMap = element->attributes(); values.resize( nnMap->length() ); for (unsigned long i = 0; i < nnMap->length(); i++) { values[i] = nnMap->item(i)->nodeName(); //TODO check if right } return values; } Poco::XML::Text* cherry::XMLMemento::GetTextNode() const { //Get the nodes Poco::XML::NodeList* nodes = element->childNodes(); unsigned long size = nodes->length(); if (size == 0) return NULL; //Search for the text node for (unsigned long index = 0; index < size; index++) { if (nodes->item(index)->nodeType() == Poco::XML::Node::TEXT_NODE) { return dynamic_cast(nodes->item(index)); } } // a Text node was not found return NULL; } void cherry::XMLMemento::PutElement(Poco::XML::Element* element, bool copyText) { Poco::XML::NodeList* nodeAttr = dynamic_cast(element->attributes()); unsigned long size = nodeAttr->length(); for (unsigned long index = 0; index < size; index++) { if (nodeAttr->item(index)->nodeType() == Poco::XML::Node::ATTRIBUTE_NODE) { Poco::XML::Attr* attr = dynamic_cast(nodeAttr->item(index)); PutString(attr->nodeName(),attr->nodeValue()); } } Poco::XML::NodeList* nodes = dynamic_cast(element->attributes()); size = nodes->length(); bool needToCopyText = copyText; for (unsigned long index = 0; index < size; index++) { unsigned short nodeType = nodes->item(index)->nodeType(); switch(nodeType) { case Poco::XML::Node::ELEMENT_NODE: { Poco::XML::Element* elem = dynamic_cast(nodes->item(index)); cherry::XMLMemento::Pointer child = CreateChild(elem->nodeName()).Cast(); child->PutElement(elem,true); } break; case Poco::XML::Node::TEXT_NODE: if (needToCopyText) { Poco::XML::Text* text = dynamic_cast(nodes->item(index)); PutTextData(text->getData()); needToCopyText = false; } break; default: break; } } } void cherry::XMLMemento::PutFloat(const std::string& key, float value) { std::stringstream ss; std::string xmlValue; ss << value; ss >> xmlValue; element->setAttribute(key, xmlValue); //element.setAttribute(key, String.valueOf(f)); } void cherry::XMLMemento::PutInteger(const std::string& key, int value) { std::stringstream ss; std::string xmlValue; ss << value; ss >> xmlValue; element->setAttribute(key, xmlValue); //element.setAttribute(key, String.valueOf(n)); } void cherry::XMLMemento::PutMemento(IMemento::Pointer memento) { // Do not copy the element's top level text node (this would overwrite the existing text). // Text nodes of children are copied. //TODO check cast throw cast error PutElement(memento.Cast()->GetElement(), false); } void cherry::XMLMemento::PutString(const std::string& key, const std::string& value) { element->setAttribute(key, value); //if (value == null) { // return;} //element.setAttribute(key, value); } void cherry::XMLMemento::PutBoolean(const std::string& key, bool value) { if (value) { element->setAttribute(key, "true"); } else { element->setAttribute(key, "false"); } } void cherry::XMLMemento::PutTextData(const std::string& data) { Poco::XML::Text* textNode = GetTextNode(); if (textNode == NULL) { textNode = factory->createTextNode(data); element->insertBefore(textNode, element->firstChild()); } else { textNode->setData(data); } } void cherry::XMLMemento::Save(cherry::XMLMemento::XMLByteOutputStream& writer) { if (writer.good()) { Poco::XML::DOMWriter* out = new Poco::XML::DOMWriter(); out->setOptions(3); //write declaration and pretty print out->writeNode(writer, factory); + delete out; } else { //TODO } } Poco::XML::Element* cherry::XMLMemento::GetElement() const { return element; } diff --git a/openCherry/Testing/CMakeLists.txt b/openCherry/Testing/CMakeLists.txt index 81e9f6f5be..47e58759cd 100755 --- a/openCherry/Testing/CMakeLists.txt +++ b/openCherry/Testing/CMakeLists.txt @@ -1,9 +1,10 @@ INCLUDE("${OPENCHERRY_BINARY_DIR}/openCherryConfig.cmake") SET(TEST_BUNDLES org.opencherry.osgi + org.opencherry.core.runtime ) FOREACH(testbundle ${TEST_BUNDLES}) ADD_SUBDIRECTORY(${testbundle}) ENDFOREACH(testbundle ${TEST_BUNDLES}) \ No newline at end of file diff --git a/openCherry/Testing/org.opencherry.core.runtime/CMakeLists.txt b/openCherry/Testing/org.opencherry.core.runtime/CMakeLists.txt new file mode 100644 index 0000000000..1731f3f9a2 --- /dev/null +++ b/openCherry/Testing/org.opencherry.core.runtime/CMakeLists.txt @@ -0,0 +1,13 @@ +INCLUDE(${OPENCHERRY_SOURCE_DIR}/CMake/cherryTestingHelpers.cmake) +INCLUDE_DIRECTORIES(${OPENCHERRY_PLUGINS_SOURCE_DIR}/org.opencherry.osgi/src) +INCLUDE_DIRECTORIES(${OPENCHERRY_PLUGINS_SOURCE_DIR}/org.opencherry.core.runtime/src) +INCLUDE_DIRECTORIES(${OPENCHERRY_PLUGINS_SOURCE_DIR}/org.opencherry.core.runtime/src/internal) + +# tests with no extra command line parameter +SET(OPENCHERRY_TESTS + cherryPreferencesServiceTest.cpp + cherryPreferencesTest.cpp + cherryXMLPreferencesStorageTest.cpp +) + +OPENCHERRY_CREATE_TESTS() \ No newline at end of file diff --git a/openCherry/Testing/org.opencherry.core.runtime/cherryPreferencesServiceTest.cpp b/openCherry/Testing/org.opencherry.core.runtime/cherryPreferencesServiceTest.cpp new file mode 100644 index 0000000000..69cc0c79df --- /dev/null +++ b/openCherry/Testing/org.opencherry.core.runtime/cherryPreferencesServiceTest.cpp @@ -0,0 +1,135 @@ +/*========================================================================= + + Program: openCherry Platform + Language: C++ + Date: $Date: 2009-02-20 18:47:40 +0100 (Fr, 20 Feb 2009) $ + Version: $Revision: 16370 $ + + Copyright (c) German Cancer Research Center, Division of Medical and + Biological Informatics. All rights reserved. + See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + + =========================================================================*/ + +#include "cherryTestingMacros.h" +#include "cherryPreferencesService.h" + +#include "mbilog.h" + +#include "Poco/File.h" +#include "Poco/Path.h" +#include "Poco/AutoPtr.h" + +#include +#include +#include + +using namespace std; + +int cherryPreferencesServiceTest(int /*argc*/, char* /*argv*/[]) +{ + CHERRY_TEST_BEGIN("PreferencesService") + Poco::File sysPrefFile; + Poco::File user1PrefFile; + Poco::File user2PrefFile; + + std::string userName1 = "testUser1"; + std::string userName2 = "testUser2"; + + vector validUsersVector; + validUsersVector.push_back(""); + validUsersVector.push_back(userName1); + validUsersVector.push_back(userName2); + + try + { + + //std::string defaultPrefDir = cherry::PreferencesService::GetDefaultPreferencesDirPath(); + std::string defaultPrefDir = "testPrefDir"; + Poco::File prefDir("testPrefDir"); + + // tidy up from prior tests + if(prefDir.exists()) + prefDir.remove(); + { + cherry::PreferencesService::Pointer prefService(new cherry::PreferencesService(prefDir.path())); + + // hopefully tested + std::string defaultPrefFileName = cherry::PreferencesService::GetDefaultPreferencesFileName(); + + //# remove old user and sys files (if their was a previous test + ostringstream s; s << defaultPrefDir << Poco::Path::separator() << defaultPrefFileName; + sysPrefFile = s.str(); + s.str(""); s << defaultPrefDir << Poco::Path::separator() << userName1 << defaultPrefFileName; + user1PrefFile = s.str(); + s.str(""); s << defaultPrefDir << Poco::Path::separator() << userName2 << defaultPrefFileName; + user2PrefFile = s.str(); + + //# test GetSystemPreferences + cherry::IPreferences::Pointer sysPrefsRoot = prefService->GetSystemPreferences(); + CHERRY_TEST_CONDITION(sysPrefsRoot.IsNotNull(), "sysPrefsRoot.IsNotNull()") + cherry::IPreferences::Pointer sysPrefsGeneral = sysPrefsRoot->Node("/general"); + sysPrefsGeneral->Put("font-size", "10"); + + //# test GetUserPreferences + cherry::IPreferences::Pointer user1PrefsRoot = prefService->GetUserPreferences(userName1); + CHERRY_TEST_CONDITION(user1PrefsRoot.IsNotNull(), "user1PrefsRoot.IsNotNull()") + cherry::IPreferences::Pointer user1PrefsGeneral = user1PrefsRoot->Node("/general"); + user1PrefsGeneral->PutInt("font-size", 10); + + //# test GetUserPreferences + cherry::IPreferences::Pointer user2PrefsRoot = prefService->GetUserPreferences(userName2); + CHERRY_TEST_CONDITION(user2PrefsRoot.IsNotNull(), "user2PrefsRoot.IsNotNull()") + cherry::IPreferences::Pointer user2PrefsGeneral = user2PrefsRoot->Node("/general"); + user2PrefsGeneral->PutFloat("font-size", 10.32324f); + + //# test GetUsers + vector usersVector = prefService->GetUsers(); + CHERRY_TEST_CONDITION(usersVector == validUsersVector, "usersVector == validUsersVector") + } + // pref service was destructed files should exist now + CHERRY_TEST_CONDITION(sysPrefFile.exists(), "sysPrefFile.exists()") + CHERRY_TEST_CONDITION(user1PrefFile.exists(), "user1PrefFile.exists()") + CHERRY_TEST_CONDITION(user2PrefFile.exists(), "user2PrefFile.exists()") + + // now create a new pref service that reads the files in again + cherry::PreferencesService::Pointer prefService(new cherry::PreferencesService()); + + //# test GetUsers + vector usersVector = prefService->GetUsers(); + CHERRY_TEST_CONDITION(usersVector == validUsersVector, "usersVector == validUsersVector") + + //# test GetSystemPreferences + cherry::IPreferences::Pointer sysPrefsRoot = prefService->GetSystemPreferences(); + CHERRY_TEST_CONDITION(sysPrefsRoot.IsNotNull(), "sysPrefsRoot.IsNotNull()") + cherry::IPreferences::Pointer sysPrefsGeneral = sysPrefsRoot->Node("/general"); + CHERRY_TEST_CONDITION(sysPrefsGeneral->Get("font-size", "") == "10", "sysPrefsGeneral->Get(\"font-size\", \"\") == \"10\"") + + //# test GetUserPreferences + cherry::IPreferences::Pointer user1PrefsRoot = prefService->GetUserPreferences(userName1); + CHERRY_TEST_CONDITION(user1PrefsRoot.IsNotNull(), "user1PrefsRoot.IsNotNull()") + cherry::IPreferences::Pointer user1PrefsGeneral = user1PrefsRoot->Node("/general"); + CHERRY_TEST_CONDITION(user1PrefsGeneral->GetInt("font-size", 0) == 10, "user1PrefsGeneral->GetInt(\"font-size\", 0)") + + //# test GetUserPreferences + cherry::IPreferences::Pointer user2PrefsRoot = prefService->GetUserPreferences(userName2); + CHERRY_TEST_CONDITION(user2PrefsRoot.IsNotNull(), "user2PrefsRoot.IsNotNull()") + cherry::IPreferences::Pointer user2PrefsGeneral = user2PrefsRoot->Node("/general"); + float user2FontSize = user2PrefsGeneral->GetFloat("font-size", 0.0f); + CHERRY_TEST_CONDITION(user2FontSize == 10.32324f, "user2PrefsGeneral->GetFloat(\"font-size\", 0.0f) == 10.32324f") + + if(prefDir.exists()) + prefDir.remove(); + + } + catch (Poco::Exception& e) + { + LOG_WARN("PreferencesService") << e.message(); + } + + CHERRY_TEST_END() +} diff --git a/openCherry/Testing/org.opencherry.core.runtime/cherryPreferencesTest.cpp b/openCherry/Testing/org.opencherry.core.runtime/cherryPreferencesTest.cpp new file mode 100644 index 0000000000..37c4cdb287 --- /dev/null +++ b/openCherry/Testing/org.opencherry.core.runtime/cherryPreferencesTest.cpp @@ -0,0 +1,424 @@ +/*========================================================================= + + Program: openCherry Platform + Language: C++ + Date: $Date: 2009-02-20 18:47:40 +0100 (Fr, 20 Feb 2009) $ + Version: $Revision: 16370 $ + + Copyright (c) German Cancer Research Center, Division of Medical and + Biological Informatics. All rights reserved. + See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + + =========================================================================*/ + +#include "cherryTestingMacros.h" +#include "cherryPreferences.h" +#include "cherryAbstractPreferencesStorage.h" + +#include "mbilog.h" + +#include "Poco/File.h" +#include "Poco/Path.h" + +#include +#include +#include + +using namespace std; + +namespace cherry +{ + class TestPreferencesStorage: public cherry::AbstractPreferencesStorage + { + + public: + /// + /// For use with cherry::SmartPtr + /// + cherryObjectMacro(cherry::TestPreferencesStorage) + /// + /// Construct a new XML-based PreferencesStorage + /// + TestPreferencesStorage(const Poco::File& _File) + : AbstractPreferencesStorage(_File) + { + this->m_Root = new Preferences(Preferences::PropertyMap(), "", 0, this); + } + + /// + /// To be implemented in the subclasses. + /// + virtual void Flush(IPreferences* _Preferences) throw(Poco::Exception, BackingStoreException) + { + std::cout << "would flush to " << this->m_File.path() << std::endl; + } + }; +} + +int cherryPreferencesTest(int /*argc*/, char* /*argv*/[]) +{ + CHERRY_TEST_BEGIN("Preferences") + + cherry::IPreferences::Pointer root(0); + cherry::TestPreferencesStorage::Pointer _PreferencesStorage(0); + + // hopefully tested + Poco::File prefDir(Poco::Path::home()+Poco::Path::separator()+".BlueBerryTest"+Poco::Path::separator()+"prefs.xml"); + + // testing methods + // TestPreferencesStorage::TestPreferencesStorage() + // AbstractPreferencesStorage::AbstractPreferencesStorage(), + // AbstractPreferencesStorage::GetRoot + { + _PreferencesStorage = new cherry::TestPreferencesStorage(Poco::Path(prefDir.path())); + root = _PreferencesStorage->GetRoot(); + CHERRY_TEST_CONDITION(root.IsNotNull(), "root.IsNotNull()") + } + + // testing methods + // Preferences::Node() + // Preferences::NodeExists() + // Preferences::Parent() + // Preferences::ChildrenNames() + // Preferences::RemoveNode() + { + LOG_INFO << "testing Preferences::Node(), Preferences::NodeExists(), Preferences::Parent(), " + "Preferences::ChildrenNames(), Preferences::RemoveNode()"; + + cherry::IPreferences::Pointer editorsNode(0); + editorsNode = root->Node("/editors"); + CHERRY_TEST_CONDITION(editorsNode.IsNotNull(), "editorsNode.IsNotNull()") + + CHERRY_TEST_CONDITION(editorsNode->NodeExists("/editors"), "editorsNode->NodeExists(\"/editors\")") + + CHERRY_TEST_CONDITION(editorsNode->Parent() == root, "editorsNode->Parent() == root") + + cherry::IPreferences::Pointer editorsGeneralNode = root->Node("/editors/general"); + CHERRY_TEST_CONDITION(editorsNode->NodeExists("/editors/general"), "editorsNode->NodeExists(\"/editors/general\")") + + cherry::IPreferences::Pointer editorsSyntaxNode = root->Node("/editors/syntax"); + CHERRY_TEST_CONDITION(editorsGeneralNode->NodeExists("/editors/syntax"), "editorsGeneralNode->NodeExists(\"/editors/syntax\")") + + cherry::IPreferences::Pointer editorsFontNode = root->Node("/editors/font"); + CHERRY_TEST_CONDITION(editorsSyntaxNode->NodeExists("/editors/font"), "editorsSyntaxNode->NodeExists(\"/editors/font\")") + + vector childrenNames; + childrenNames.push_back("general"); + childrenNames.push_back("syntax"); + childrenNames.push_back("font"); + CHERRY_TEST_CONDITION(editorsNode->ChildrenNames() == childrenNames, "editorsNode->ChildrenNames() == childrenName") + + editorsFontNode->RemoveNode(); + CHERRY_TEST_FOR_EXCEPTION(Poco::IllegalStateException, editorsFontNode->Parent()); + } + + // testing methods + // Preferences::put*() + // Preferences::get*() + { + LOG_INFO << "testing Preferences::put*(), Preferences::get*()"; + + CHERRY_TEST_CONDITION(root->NodeExists("/editors/general"), "root->NodeExists(\"/editors/general\")") + cherry::IPreferences::Pointer editorsGeneralNode = root->Node("/editors/general"); + + std::string strKey = "Bad words";std::string strValue = "badword1 badword2"; + editorsGeneralNode->Put(strKey, strValue); + CHERRY_TEST_CONDITION(editorsGeneralNode->Get(strKey, "") == strValue + , "editorsGeneralNode->Get(strKey, \"\")") + CHERRY_TEST_CONDITION(editorsGeneralNode->Get("wrong key", "default value") == "default value" + , "editorsGeneralNode->Get(\"wrong key\", \"default value\") == \"default value\"") + + strKey = "Show Line Numbers";bool bValue = true; + editorsGeneralNode->PutBool(strKey, bValue); + CHERRY_TEST_CONDITION(editorsGeneralNode->GetBool(strKey, !bValue) == bValue + , "editorsGeneralNode->GetBool(strKey, !bValue)") + + strKey = "backgroundcolor"; strValue = "#00FF00"; + editorsGeneralNode->PutByteArray(strKey, strValue); + CHERRY_TEST_CONDITION(editorsGeneralNode->GetByteArray(strKey, "") == strValue + , "editorsGeneralNode->GetByteArray(strKey, \"\")") + + strKey = "update time"; double dValue = 1.23; + editorsGeneralNode->PutDouble(strKey, dValue); + CHERRY_TEST_CONDITION(editorsGeneralNode->GetDouble(strKey, 0.0) == dValue + , "editorsGeneralNode->GetDouble(strKey, 0.0)") + + strKey = "update time float"; float fValue = 1.23f; + editorsGeneralNode->PutFloat(strKey, fValue); + CHERRY_TEST_CONDITION(editorsGeneralNode->GetFloat(strKey, 0.0f) == fValue + , "editorsGeneralNode->GetFloat(strKey, 0.0f)") + + strKey = "Break on column"; int iValue = 80; + editorsGeneralNode->PutInt(strKey, iValue); + CHERRY_TEST_CONDITION(editorsGeneralNode->GetInt(strKey, 0) == iValue + , "editorsGeneralNode->GetInt(strKey, 0)") + + strKey = "Maximum number of words"; long lValue = 11000000; + editorsGeneralNode->PutLong(strKey, lValue); + CHERRY_TEST_CONDITION(editorsGeneralNode->GetLong(strKey, 0) == lValue + , "editorsGeneralNode->GetLong(strKey, 0)") + } + +/* + // Message test + { + cherry::Message<> msg; + + MessageReceiver receiver; + MessageReceiver receiver2; + + typedef cherry::MessageDelegate Delegate; + + msg += Delegate(&receiver, &MessageReceiver::PureSignal); + CHERRY_TEST_CONDITION(msg.GetListeners().size() == 1, "Adding listener") + msg += Delegate(&receiver2, &MessageReceiver::PureSignal); + // duplicate entry + msg += Delegate(&receiver, &MessageReceiver::PureSignal); + CHERRY_TEST_CONDITION(msg.GetListeners().size() == 2, "Adding duplicate listener") + + msg.Send(); + CHERRY_TEST_CONDITION(receiver.received && receiver2.received, "Receiving signal") + + receiver.received = false; + receiver2.received = false; + msg -= Delegate(&receiver, &MessageReceiver::PureSignal); + CHERRY_TEST_CONDITION(msg.GetListeners().size() == 1, "Removing listener") + + msg.Send(); + CHERRY_TEST_CONDITION(receiver.received == false && receiver2.received, "Receiving signal") + } + + // Message with return type test + { + cherry::Message msg; + + MessageReceiver receiver; + typedef cherry::MessageDelegate Delegate; + msg += Delegate(&receiver, &MessageReceiver::PureSignalWithReturn); + msg(); + CHERRY_TEST_CONDITION(receiver.received, "Receiving signal with return type") + + receiver.received = false; + typedef cherry::Message::ListenerList Listeners; + const Listeners& listeners = msg.GetListeners(); + bool valueReturned = false; + for (Listeners::const_iterator iter = listeners.begin(); + iter != listeners.end(); ++iter) + { + valueReturned = (*iter)->Execute(); + if (valueReturned) break; + } + CHERRY_TEST_CONDITION(valueReturned, "Handling return values") + } + + // Message1 test + { + cherry::Message1 msg; + + MessageReceiver receiver; + MessageReceiver receiver2; + + typedef cherry::MessageDelegate1 Delegate; + + msg += Delegate(&receiver, &MessageReceiver::Message1); + CHERRY_TEST_CONDITION(msg.GetListeners().size() == 1, "Adding listener") + msg += Delegate(&receiver2, &MessageReceiver::Message1); + // duplicate entry + msg += Delegate(&receiver, &MessageReceiver::Message1); + CHERRY_TEST_CONDITION(msg.GetListeners().size() == 2, "Adding duplicate listener") + + msg.Send(1); + CHERRY_TEST_CONDITION(receiver.received && receiver2.received, "Receiving message1") + + receiver.received = false; + receiver2.received = false; + msg -= Delegate(&receiver, &MessageReceiver::Message1); + CHERRY_TEST_CONDITION(msg.GetListeners().size() == 1, "Removing listener") + + msg.Send(1); + CHERRY_TEST_CONDITION(receiver.received == false && receiver2.received, "Receiving message1") + } + + // Message1 with return type test + { + cherry::Message1 msg; + + MessageReceiver receiver; + typedef cherry::MessageDelegate1 Delegate; + msg += Delegate(&receiver, &MessageReceiver::Message1WithReturn); + msg(1); + CHERRY_TEST_CONDITION(receiver.received, "Receiving message1 with return type") + + receiver.received = false; + typedef cherry::Message1::ListenerList Listeners; + const Listeners& listeners = msg.GetListeners(); + bool valueReturned = false; + for (Listeners::const_iterator iter = listeners.begin(); + iter != listeners.end(); ++iter) + { + valueReturned = (*iter)->Execute(1); + if (valueReturned) break; + } + CHERRY_TEST_CONDITION(valueReturned, "Handling return values") + } + + // Message2 test + { + cherry::Message2 msg; + + MessageReceiver receiver; + MessageReceiver receiver2; + + typedef cherry::MessageDelegate2 Delegate; + + msg += Delegate(&receiver, &MessageReceiver::Message2); + CHERRY_TEST_CONDITION(msg.GetListeners().size() == 1, "Adding listener") + msg += Delegate(&receiver2, &MessageReceiver::Message2); + // duplicate entry + msg += Delegate(&receiver, &MessageReceiver::Message2); + CHERRY_TEST_CONDITION(msg.GetListeners().size() == 2, "Adding duplicate listener") + + msg.Send(1, 1.0); + CHERRY_TEST_CONDITION(receiver.received && receiver2.received, "Receiving message2") + + receiver.received = false; + receiver2.received = false; + msg -= Delegate(&receiver, &MessageReceiver::Message2); + CHERRY_TEST_CONDITION(msg.GetListeners().size() == 1, "Removing listener") + + msg.Send(1, 1.0); + CHERRY_TEST_CONDITION(receiver.received == false && receiver2.received, "Receiving message2") + } + + // Message2 with return type test + { + cherry::Message2 msg; + + MessageReceiver receiver; + typedef cherry::MessageDelegate2 Delegate; + msg += Delegate(&receiver, &MessageReceiver::Message2WithReturn); + msg(1, 2); + CHERRY_TEST_CONDITION(receiver.received, "Receiving message2 with return type") + + receiver.received = false; + typedef cherry::Message2::ListenerList Listeners; + const Listeners& listeners = msg.GetListeners(); + bool valueReturned = false; + for (Listeners::const_iterator iter = listeners.begin(); + iter != listeners.end(); ++iter) + { + valueReturned = (*iter)->Execute(1, 0.4); + if (valueReturned) break; + } + CHERRY_TEST_CONDITION(valueReturned, "Handling return values") + } + + // Message3 test + { + cherry::Message3 msg; + + MessageReceiver receiver; + MessageReceiver receiver2; + + typedef cherry::MessageDelegate3 Delegate; + + msg += Delegate(&receiver, &MessageReceiver::Message3); + CHERRY_TEST_CONDITION(msg.GetListeners().size() == 1, "Adding listener") + msg += Delegate(&receiver2, &MessageReceiver::Message3); + // duplicate entry + msg += Delegate(&receiver, &MessageReceiver::Message3); + CHERRY_TEST_CONDITION(msg.GetListeners().size() == 2, "Adding duplicate listener") + + msg.Send(1, 1.0, 2.0); + CHERRY_TEST_CONDITION(receiver.received && receiver2.received, "Receiving message3") + + receiver.received = false; + receiver2.received = false; + msg -= Delegate(&receiver, &MessageReceiver::Message3); + CHERRY_TEST_CONDITION(msg.GetListeners().size() == 1, "Removing listener") + + msg.Send(1, -1.0, 5.0); + CHERRY_TEST_CONDITION(receiver.received == false && receiver2.received, "Receiving message3") + } + + // Message3 with return type test + { + cherry::Message3 msg; + + MessageReceiver receiver; + typedef cherry::MessageDelegate3 Delegate; + msg += Delegate(&receiver, &MessageReceiver::Message3WithReturn); + msg(1, -2, 0.2); + CHERRY_TEST_CONDITION(receiver.received, "Receiving message3 with return type") + + receiver.received = false; + typedef cherry::Message3::ListenerList Listeners; + const Listeners& listeners = msg.GetListeners(); + bool valueReturned = false; + for (Listeners::const_iterator iter = listeners.begin(); + iter != listeners.end(); ++iter) + { + valueReturned = (*iter)->Execute(1, 23.5, -12.2); + if (valueReturned) break; + } + CHERRY_TEST_CONDITION(valueReturned, "Handling return values") + } + + // Message4 test + { + cherry::Message4 msg; + + MessageReceiver receiver; + MessageReceiver receiver2; + + typedef cherry::MessageDelegate4 Delegate; + + msg += Delegate(&receiver, &MessageReceiver::Message4); + CHERRY_TEST_CONDITION(msg.GetListeners().size() == 1, "Adding listener") + msg += Delegate(&receiver2, &MessageReceiver::Message4); + // duplicate entry + msg += Delegate(&receiver, &MessageReceiver::Message4); + CHERRY_TEST_CONDITION(msg.GetListeners().size() == 2, "Adding duplicate listener") + + msg.Send(1, 5.4, -1.0, true); + CHERRY_TEST_CONDITION(receiver.received && receiver2.received, "Receiving message4") + + receiver.received = false; + receiver2.received = false; + msg -= Delegate(&receiver, &MessageReceiver::Message4); + CHERRY_TEST_CONDITION(msg.GetListeners().size() == 1, "Removing listener") + + msg.Send(1, 0.2, 12.0, true); + CHERRY_TEST_CONDITION(receiver.received == false && receiver2.received, "Receiving message4") + } + + // Message4 with return type test + { + cherry::Message4 msg; + + MessageReceiver receiver; + typedef cherry::MessageDelegate4 Delegate; + msg += Delegate(&receiver, &MessageReceiver::Message4WithReturn); + msg(1, 4.1, -1, true); + CHERRY_TEST_CONDITION(receiver.received, "Receiving message4 with return type") + + receiver.received = false; + typedef cherry::Message4::ListenerList Listeners; + const Listeners& listeners = msg.GetListeners(); + bool valueReturned = false; + for (Listeners::const_iterator iter = listeners.begin(); + iter != listeners.end(); ++iter) + { + valueReturned = (*iter)->Execute(1, -34.21, 2, true); + if (valueReturned) break; + } + CHERRY_TEST_CONDITION(valueReturned, "Handling return values") + }*/ + + + CHERRY_TEST_END() +} diff --git a/openCherry/Testing/org.opencherry.core.runtime/cherryXMLPreferencesStorageTest.cpp b/openCherry/Testing/org.opencherry.core.runtime/cherryXMLPreferencesStorageTest.cpp new file mode 100644 index 0000000000..4f35ad4e9b --- /dev/null +++ b/openCherry/Testing/org.opencherry.core.runtime/cherryXMLPreferencesStorageTest.cpp @@ -0,0 +1,93 @@ +/*========================================================================= + + Program: openCherry Platform + Language: C++ + Date: $Date: 2009-02-20 18:47:40 +0100 (Fr, 20 Feb 2009) $ + Version: $Revision: 16370 $ + + Copyright (c) German Cancer Research Center, Division of Medical and + Biological Informatics. All rights reserved. + See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + + =========================================================================*/ + +#include "cherryTestingMacros.h" +#include "cherryXMLPreferencesStorage.h" + +using namespace std; + +void testLoadFile(Poco::File& file) +{ + // file exists, test parsing etc. + cherry::XMLPreferencesStorage storage(file); + cherry::IPreferences::Pointer prefRoot = storage.GetRoot(); + CHERRY_TEST_CONDITION(prefRoot.IsNotNull(), "prefRoot.IsNotNull()") + + // try to find nodes that were create by testCreateFile + CHERRY_TEST_CONDITION(prefRoot->NodeExists("/Text Editor"), "prefRoot.NodeExists(\"/Text Editor\")") + CHERRY_TEST_CONDITION(prefRoot->NodeExists("/Text Editor"), "prefRoot.NodeExists(\"/Text Editor/C++\")") + CHERRY_TEST_CONDITION(prefRoot->NodeExists("/Text Editor"), "prefRoot.NodeExists(\"/Text Editor/Java\")") + CHERRY_TEST_CONDITION(prefRoot->NodeExists("/Text Editor"), "prefRoot.NodeExists(\"/Text Editor/Java/Javascript\")") + + // try to get some properties + cherry::IPreferences::Pointer cppEditor = prefRoot->Node("/Text Editor/C++"); + CHERRY_TEST_CONDITION(cppEditor->GetBool("Show Line Numbers", false) == true, "cppEditor->GetBool(\"Show Line Numbers\")") + CHERRY_TEST_CONDITION(cppEditor->Get("File Extension", "") == "cpp cxx", "cppEditor->Get(\"File Extension\") == \"cpp cxx\"") + + cherry::IPreferences::Pointer jsEditor = prefRoot->Node("/Text Editor/Java/Javascript"); + CHERRY_TEST_CONDITION(jsEditor->GetBool("Show Line Numbers", false) == true, "jsEditor->GetBool(\"Show Line Numbers\")") + CHERRY_TEST_CONDITION(jsEditor->Get("File Extension", "") == "js", "jsEditor->Get(\"File Extension\") == \"js\"") + CHERRY_TEST_CONDITION(jsEditor->GetInt("Show margin at column", 0) == 70, "jsEditor->GetInt(\"Show margin at column\", 0) == 70") +} + +void testCreateFile(Poco::File& file) +{ + // file does not exist, test with empty preferences + cherry::XMLPreferencesStorage storage(file); + cherry::IPreferences::Pointer prefRoot = storage.GetRoot(); + CHERRY_TEST_CONDITION(prefRoot.IsNotNull(), "prefRoot.IsNotNull()") + + // add some preferences ( tested in the preferences test -> no conditions here ) + cherry::IPreferences::Pointer textEditor = prefRoot->Node("/Text Editor"); + textEditor->PutBool("Show Line Numbers", true); + textEditor->Put("File Extension", "txt rtf xml"); + + cherry::IPreferences::Pointer cppEditor = prefRoot->Node("/Text Editor/C++"); + cppEditor->PutBool("Show Line Numbers", true); + cppEditor->Put("File Extension", "cpp cxx"); + cppEditor->PutInt("Show margin at column", 80); + + cherry::IPreferences::Pointer javaEditor = prefRoot->Node("/Text Editor/Java"); + javaEditor->PutBool("Show Line Numbers", false); + javaEditor->Put("File Extension", "java"); + javaEditor->PutInt("Show margin at column", 90); + + cherry::IPreferences::Pointer jsEditor = prefRoot->Node("/Text Editor/Java/Javascript"); + jsEditor->PutBool("Show Line Numbers", true); + jsEditor->Put("File Extension", "js"); + jsEditor->PutInt("Show margin at column", 70); + + // try to flush it with an arbitrary node (should take the root node) + storage.Flush(jsEditor.GetPointer()); + CHERRY_TEST_CONDITION(file.exists(), "file.exists()") +} + +int cherryXMLPreferencesStorageTest(int /*argc*/, char* /*argv*/[]) +{ + CHERRY_TEST_BEGIN("XMLPreferencesStorage") + + Poco::Path path("prefs.xml"); + Poco::File file(path); + + if(file.exists()) + file.remove(); + + testCreateFile(file); + testLoadFile(file); + + CHERRY_TEST_END() +}