Difference between revisions of "Scripting API (Preview)"
Irajawapys (talk | contribs) |
m (Reverted edits by Irajawapys (Talk) to last version by Boercher) |
||
Line 1: | Line 1: | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
This page describes a development version which will only work with preview/alpha/beta/test versions of Freeplane. If you are seeking for the stable released version see [[Scripting API]]. | This page describes a development version which will only work with preview/alpha/beta/test versions of Freeplane. If you are seeking for the stable released version see [[Scripting API]]. | ||
Line 12: | Line 4: | ||
The development is currently driven by the implementation of the [[Formula]] feature that provides mindmappers with features you know from spreadsheet processors like Excel. This implies | The development is currently driven by the implementation of the [[Formula]] feature that provides mindmappers with features you know from spreadsheet processors like Excel. This implies | ||
− | * The syntax must allow more concise statements. For instance to convert a node text to a number you can now write | + | * The syntax must allow more concise statements. For instance to convert a node text to a number you can now write <tt>to.num</tt> instead of <tt>Double.parse(node.text)</tt>. Attributes are available as <tt>node['name']</tt> in addition to the old <tt>node.attributes.get('name')</tt>. |
* The API must provide additional functionality that is needed for formulas. | * The API must provide additional functionality that is needed for formulas. | ||
* The API must be re-organized to provide a read-only API for formulas. | * The API must be re-organized to provide a read-only API for formulas. | ||
===Extended Namespace for Scripts=== | ===Extended Namespace for Scripts=== | ||
− | Methods and attributes of the current node are directly available to a script so instead of | + | Methods and attributes of the current node are directly available to a script so instead of <tt>node.children.size()</tt> you may simply write <tt>children.size()</tt>. |
Additionally the following methods are directly available: | Additionally the following methods are directly available: | ||
− | + | <groovy> | |
/** Shortcut for node.map.node(id) - necessary for ids to other maps. */ | /** Shortcut for node.map.node(id) - necessary for ids to other maps. */ | ||
Line 34: | Line 26: | ||
/** returns valueIfNull if value is null and value otherwise. */ | /** returns valueIfNull if value is null and value otherwise. */ | ||
public Object ifNull(Object value, Object valueIfNull); | public Object ifNull(Object value, Object valueIfNull); | ||
− | + | </groovy> | |
===Convertible=== | ===Convertible=== | ||
A very important usability improvement, not only for formula writers, is the introduction of the class [[Scripting: Convertible|Convertible]] that is returned now by some new methods/properties: | A very important usability improvement, not only for formula writers, is the introduction of the class [[Scripting: Convertible|Convertible]] that is returned now by some new methods/properties: | ||
− | * | + | * <tt>node.to</tt> (or <tt>node.getTo())</tt> |
− | * | + | * <tt>node['attr_name']</tt> (or <tt>node.getAt('attr_name'))</tt> |
− | * | + | * <tt>node.note</tt> (or <tt>node.getNote())</tt> |
===Extended Setters=== | ===Extended Setters=== | ||
− | Many | + | Many "setters" on the other hand, like <tt>node.setText()</tt> have been extended to accept not only Strings but Objects. Much effort was spent to ensure that this conversion matches the conversions that <tt>Convertible</tt> performs for Strings. For example <tt>node.text = new Date()</tt> is converted to <tt>2010-10-05T22:11:03.243+0000</tt> which <tt>Convertible</tt> knows how to convert back to date (try <tt>node.to.date</tt>). |
− | For formulas it's important that the formulas itself don't change the state of the map. Currently only the first step is made: All subinterfaces | + | For formulas it's important that the formulas itself don't change the state of the map. Currently only the first step is made: All subinterfaces <tt>Xyz</tt> have a base interface <tt>XyzRO</tt> that includes only the methods that are suitable for formulas. The Proxy implementations implement the full interfaces currently and the constraint is not enforced. |
===New Free Functions=== | ===New Free Functions=== | ||
Line 53: | Line 45: | ||
Some controller methods were introduced mainly for testing: | Some controller methods were introduced mainly for testing: | ||
− | * | + | * <tt>Controller.newMap()</tt> |
− | * | + | * <tt>Controller.undo()</tt> |
− | * | + | * <tt>Controller.redo()</tt> |
==Example Maps== | ==Example Maps== | ||
Line 63: | Line 55: | ||
Each script is given two variables: | Each script is given two variables: | ||
− | + | <groovy> | |
final Proxy.Node node; | final Proxy.Node node; | ||
final Proxy.Controller c; | final Proxy.Controller c; | ||
− | + | </groovy> | |
− | ''New:'' Methods and properties of the current node are directly available so you can write | + | ''New:'' Methods and properties of the current node are directly available so you can write <tt>children.size()</tt> instead of <tt>node.children.size()</tt>. |
==Interface== | ==Interface== | ||
− | + | <groovy> | |
package org.freeplane.plugin.script.proxy; | package org.freeplane.plugin.script.proxy; | ||
Line 99: | Line 91: | ||
String get(final String name); | String get(final String name); | ||
− | /** returns the | + | /** returns the <em>first</em> value of an attribute with the given name or null otherwise. @since 1.2 */ |
String getFirst(final String name); | String getFirst(final String name); | ||
/** returns all values for the attribute name. */ | /** returns all values for the attribute name. */ | ||
− | List | + | List<String> getAll(final String name); |
/** returns all attribute names in the proper sequence. The number of names returned | /** returns all attribute names in the proper sequence. The number of names returned | ||
* is equal to the number of attributes. | * is equal to the number of attributes. | ||
− | * | + | * <pre> |
* // rename attribute | * // rename attribute | ||
* int i = 0; | * int i = 0; | ||
* for (String name : attributes.getAttributeNames()) { | * for (String name : attributes.getAttributeNames()) { | ||
− | * if (name.equals( | + | * if (name.equals("xy")) |
− | * attributes.set(i, | + | * attributes.set(i, "xyz", attributes.get(i)); |
* ++i; | * ++i; | ||
* } | * } | ||
− | * | + | * </pre> */ |
− | List | + | List<String> getAttributeNames(); |
/** returns the attribute value at the given index. | /** returns the attribute value at the given index. | ||
− | * @throws IndexOutOfBoundsException if index is out of range | + | * @throws IndexOutOfBoundsException if index is out of range <tt>(index |
− | * & | + | * < 0 || index >= size())</tt>.*/ |
String get(final int index); | String get(final int index); | ||
Line 127: | Line 119: | ||
/** returns the index of the first attribute with the given name if one exists or -1 otherwise. | /** returns the index of the first attribute with the given name if one exists or -1 otherwise. | ||
− | * For searches for | + | * For searches for <em>all</em> attributes with a given name <code>getAttributeNames()</code> |
* must be used. @since 1.2*/ | * must be used. @since 1.2*/ | ||
int findFirst(final String name); | int findFirst(final String name); | ||
− | /** the number of attributes. It is | + | /** the number of attributes. It is <code>size() == getAttributeNames().size()</code>. */ |
int size(); | int size(); | ||
} | } | ||
Line 139: | Line 131: | ||
interface Attributes extends AttributesRO { | interface Attributes extends AttributesRO { | ||
/** sets the value of the attribute at an index. This method will not create new attributes. | /** sets the value of the attribute at an index. This method will not create new attributes. | ||
− | * @throws IndexOutOfBoundsException if index is out of range | + | * @throws IndexOutOfBoundsException if index is out of range <tt>(index |
− | * & | + | * < 0 || index >= size())</tt>. */ |
void set(final int index, final String value); | void set(final int index, final String value); | ||
/** sets name and value of the attribute at the given index. This method will not create new attributes. | /** sets name and value of the attribute at the given index. This method will not create new attributes. | ||
− | * @throws IndexOutOfBoundsException if index is out of range | + | * @throws IndexOutOfBoundsException if index is out of range <tt>(index |
− | * & | + | * < 0 || index >= size())</tt>. */ |
void set(final int index, final String name, final String value); | void set(final int index, final String name, final String value); | ||
− | /** removes the | + | /** removes the <em>first</em> attribute with this name. |
* @returns true on removal of an existing attribute and false otherwise. | * @returns true on removal of an existing attribute and false otherwise. | ||
* @deprecated before 1.1 - use remove(int) or removeAll(String) instead. */ | * @deprecated before 1.1 - use remove(int) or removeAll(String) instead. */ | ||
Line 154: | Line 146: | ||
boolean remove(final String name); | boolean remove(final String name); | ||
− | /** removes | + | /** removes <em>all</em> attributes with this name. |
* @returns true on removal of an existing attribute and false otherwise. */ | * @returns true on removal of an existing attribute and false otherwise. */ | ||
boolean removeAll(final String name); | boolean removeAll(final String name); | ||
/** removes the attribute at the given index. | /** removes the attribute at the given index. | ||
− | * @throws IndexOutOfBoundsException if index is out of range | + | * @throws IndexOutOfBoundsException if index is out of range <tt>(index |
− | * & | + | * < 0 || index >= size())</tt>. */ |
void remove(final int index); | void remove(final int index); | ||
/** adds an attribute if there is no attribute with the given name or changes | /** adds an attribute if there is no attribute with the given name or changes | ||
− | * the value | + | * the value <em>of the first</em> attribute with the given name. */ |
void set(final String name, final String value); | void set(final String name, final String value); | ||
Line 215: | Line 207: | ||
Node getSelected(); | Node getSelected(); | ||
− | List | + | List<Node> getSelecteds(); |
− | /** returns List | + | /** returns List<Node> of Node objects sorted on Y |
* | * | ||
* @param differentSubtrees if true | * @param differentSubtrees if true | ||
* children/grandchildren/grandgrandchildren/... nodes of selected | * children/grandchildren/grandgrandchildren/... nodes of selected | ||
* parent nodes are excluded from the result. */ | * parent nodes are excluded from the result. */ | ||
− | List | + | List<Node> getSortedSelection(boolean differentSubtrees); |
/** | /** | ||
* returns Freeplane version. | * returns Freeplane version. | ||
* Use it like this: | * Use it like this: | ||
− | * | + | * <pre> |
* import org.freeplane.core.util.FreeplaneVersion | * import org.freeplane.core.util.FreeplaneVersion | ||
* import org.freeplane.core.ui.components.UITools | * import org.freeplane.core.ui.components.UITools | ||
* | * | ||
− | * def required = FreeplaneVersion.getVersion( | + | * def required = FreeplaneVersion.getVersion("1.1.2"); |
− | * if (c.freeplaneVersion | + | * if (c.freeplaneVersion < required) |
− | * UITools.errorMessage( | + | * UITools.errorMessage("Freeplane version " + c.freeplaneVersion |
− | * + | + | * + " not supported - update to at least " + required); |
− | * | + | * </pre> |
*/ | */ | ||
FreeplaneVersion getFreeplaneVersion(); | FreeplaneVersion getFreeplaneVersion(); | ||
/** Starting from the root node, recursively searches for nodes for which | /** Starting from the root node, recursively searches for nodes for which | ||
− | * | + | * <code>condition.checkNode(node)</code> returns true. |
* @see Node.find(ICondition) for searches on subtrees | * @see Node.find(ICondition) for searches on subtrees | ||
* @deprecated since 1.2 use find(Closure) instead. */ | * @deprecated since 1.2 use find(Closure) instead. */ | ||
− | List | + | List<Node> find(ICondition condition); |
/** | /** | ||
− | * Starting from the root node, recursively searches for nodes for which | + | * Starting from the root node, recursively searches for nodes for which <code>closure.call(node)</code> |
* returns true. | * returns true. | ||
− | * | + | * <p> |
− | * A find method that uses a Groovy closure ( | + | * A find method that uses a Groovy closure ("block") for simple custom searches. As this closure |
− | * will be called with a node as an argument (to be referenced by | + | * will be called with a node as an argument (to be referenced by <code>it</code>) the search can |
* evaluate every node property, like attributes, icons, node text or notes. | * evaluate every node property, like attributes, icons, node text or notes. | ||
− | * | + | * <p> |
* Examples: | * Examples: | ||
− | * | + | * <pre> |
* def nodesWithNotes = c.find{ it.noteText != null } | * def nodesWithNotes = c.find{ it.noteText != null } | ||
* | * | ||
− | * def matchingNodes = c.find{ it.text.matches( | + | * def matchingNodes = c.find{ it.text.matches(".*\\d.*") } |
* def texts = matchingNodes.collect{ it.text } | * def texts = matchingNodes.collect{ it.text } | ||
− | * print | + | * print "node texts containing numbers:\n " + texts.join("\n ") |
− | * | + | * </pre> |
* @param closure a Groovy closure that returns a boolean value. The closure will receive | * @param closure a Groovy closure that returns a boolean value. The closure will receive | ||
* a NodeModel as an argument which can be tested for a match. | * a NodeModel as an argument which can be tested for a match. | ||
− | * @return all nodes for which | + | * @return all nodes for which <code>closure.call(NodeModel)</code> returns true. |
* @see Node.find(Closure) for searches on subtrees | * @see Node.find(Closure) for searches on subtrees | ||
*/ | */ | ||
− | List | + | List<Node> find(Closure closure); |
} | } | ||
Line 277: | Line 269: | ||
void selectBranch(Node branchRoot); | void selectBranch(Node branchRoot); | ||
− | /** toSelect is a List | + | /** toSelect is a List<Node> of Node objects */ |
− | void selectMultipleNodes(List | + | void selectMultipleNodes(List<Node> toSelect); |
/** reset undo / redo lists and deactivate Undo for current script */ | /** reset undo / redo lists and deactivate Undo for current script */ | ||
Line 289: | Line 281: | ||
void redo(); | void redo(); | ||
− | /** The main info for the status line with key= | + | /** The main info for the status line with key="standard", use null to remove. Removes icon if there is one. */ |
void setStatusInfo(String info); | void setStatusInfo(String info); | ||
Line 296: | Line 288: | ||
void setStatusInfo(String infoPanelKey, String info); | void setStatusInfo(String infoPanelKey, String info); | ||
− | /** Info for status line - text and icon - null stands for | + | /** Info for status line - text and icon - null stands for "remove" (text or icon) |
− | * @param infoPanelKey | + | * @param infoPanelKey "standard" is the left most standard info panel. If a panel with |
* this name doesn't exist it will be created. | * this name doesn't exist it will be created. | ||
* @param info Info text | * @param info Info text | ||
* @param iconKey key as those that are used for nodes (see {@link Icons#addIcon(String)}). | * @param iconKey key as those that are used for nodes (see {@link Icons#addIcon(String)}). | ||
− | * | + | * <pre> |
− | * println( | + | * println("all available icon keys: " + FreeplaneIconUtils.listStandardIconKeys()) |
− | * c.setStatusInfo( | + | * c.setStatusInfo("standard", "hi there!", "button_ok"); |
− | * | + | * </pre> |
* @see FreeplaneIconUtils | * @see FreeplaneIconUtils | ||
* @since 1.2 */ | * @since 1.2 */ | ||
Line 332: | Line 324: | ||
void setType(EdgeStyle type); | void setType(EdgeStyle type); | ||
− | /** can be -1 for default, 0 for thin, | + | /** can be -1 for default, 0 for thin, >0 */ |
void setWidth(int width); | void setWidth(int width); | ||
} | } | ||
Line 387: | Line 379: | ||
interface IconsRO { | interface IconsRO { | ||
− | /** returns List | + | /** returns List<Node> of Strings (corresponding to iconID above); |
− | * iconID is one of | + | * iconID is one of "Idea","Question","Important", etc. */ |
− | List | + | List<String> getIcons(); |
} | } | ||
Line 396: | Line 388: | ||
* adds an icon to a node if an icon for the given key can be found. The same icon can be added multiple | * adds an icon to a node if an icon for the given key can be found. The same icon can be added multiple | ||
* times. | * times. | ||
− | * | + | * <pre> |
− | * println( | + | * println("all available icon keys: " + FreeplaneIconUtils.listStandardIconKeys()) |
− | * node.icons.addIcon( | + | * node.icons.addIcon("button_ok") |
− | * | + | * </pre> |
* @see FreeplaneIconUtils */ | * @see FreeplaneIconUtils */ | ||
void addIcon(String name); | void addIcon(String name); | ||
Line 415: | Line 407: | ||
/** target is a URI. | /** target is a URI. | ||
* An empty String will remove the link. | * An empty String will remove the link. | ||
− | * To get a local link (i.e. to another node) target should be: | + | * To get a local link (i.e. to another node) target should be: "#" + nodeId */ |
boolean set(String target); | boolean set(String target); | ||
} | } | ||
Line 440: | Line 432: | ||
* closes a map. Note that there is no undo for this method. | * closes a map. Note that there is no undo for this method. | ||
* @param close map even if there are unsaved changes. | * @param close map even if there are unsaved changes. | ||
− | * @param allowInteraction if (allowInteraction & | + | * @param allowInteraction if (allowInteraction && ! force) a saveAs dialog will be opened if there are |
* unsaved changes. | * unsaved changes. | ||
* @return false if the saveAs was cancelled by the user and true otherwise. | * @return false if the saveAs was cancelled by the user and true otherwise. | ||
Line 464: | Line 456: | ||
* {@link Convertible}, not a String. Nevertheless it behaves like a String in almost all respects, | * {@link Convertible}, not a String. Nevertheless it behaves like a String in almost all respects, | ||
* that is, in Groovy scripts it understands all String methods like lenght(), matches() etc. | * that is, in Groovy scripts it understands all String methods like lenght(), matches() etc. | ||
− | * | + | * <pre> |
* // standard way | * // standard way | ||
− | * node.attributes.set( | + | * node.attributes.set("attribute name", "12") |
* // implicitely use getAt() | * // implicitely use getAt() | ||
− | * def val = node[ | + | * def val = node["attribute name"] |
* // use all conversions that Convertible provides (num, date, string, ...) | * // use all conversions that Convertible provides (num, date, string, ...) | ||
* assert val.num == new Long(12) | * assert val.num == new Long(12) | ||
* // or use it just like a string | * // or use it just like a string | ||
− | * assert val.startsWith( | + | * assert val.startsWith("1") |
− | * | + | * </pre> |
* @since 1.2 | * @since 1.2 | ||
*/ | */ | ||
Line 484: | Line 476: | ||
/** returns the children of this node ordered by Y coordinate. */ | /** returns the children of this node ordered by Y coordinate. */ | ||
− | List | + | List<Node> getChildren(); |
− | Collection | + | Collection<Connector> getConnectorsIn(); |
− | Collection | + | Collection<Connector> getConnectorsOut(); |
ExternalObject getExternalObject(); | ExternalObject getExternalObject(); | ||
Line 541: | Line 533: | ||
/** | /** | ||
* returns an object that performs conversions (method name is choosen to give descriptive code): | * returns an object that performs conversions (method name is choosen to give descriptive code): | ||
− | * | + | * <dl> |
− | * | + | * <dt>node.to.num <dd>Long or Double, see {@link Convertible#getDate()}. |
− | * | + | * <dt>node.to.date <dd>Date, see {@link Convertible#getDate()}. |
− | * | + | * <dt>node.to.string <dd>Text, see {@link Convertible#getString()}. |
− | * | + | * <dt>node.to.text <dd>an alias for getString(), see {@link Convertible#getText()}. |
− | * | + | * <dt>node.to.object <dd>returns what fits best, see {@link Convertible#getObject()}. |
− | * | + | * </dl> |
* Note that parse errors result in {@link ConversionException}s. | * Note that parse errors result in {@link ConversionException}s. | ||
* @return ConvertibleObject | * @return ConvertibleObject | ||
Line 557: | Line 549: | ||
Convertible getValue(); | Convertible getValue(); | ||
− | /** returns true if p is a parent, or grandparent, ... of this node, or if it | + | /** returns true if p is a parent, or grandparent, ... of this node, or if it <em>is equal<em> |
* to this node; returns false otherwise. */ | * to this node; returns false otherwise. */ | ||
boolean isDescendantOf(Node p); | boolean isDescendantOf(Node p); | ||
Line 572: | Line 564: | ||
/** Starting from this node, recursively searches for nodes for which | /** Starting from this node, recursively searches for nodes for which | ||
− | * | + | * <code>condition.checkNode(node)</code> returns true. |
* @deprecated since 1.2 use find(Closure) instead. */ | * @deprecated since 1.2 use find(Closure) instead. */ | ||
− | List | + | List<Node> find(ICondition condition); |
− | /** Starting from this node, recursively searches for nodes for which | + | /** Starting from this node, recursively searches for nodes for which <code>closure.call(node)</code> |
* returns true. See {@link Controller#find(Closure)} for details. */ | * returns true. See {@link Controller#find(Closure)} for details. */ | ||
− | List | + | List<Node> find(Closure closure); |
Date getLastModifiedAt(); | Date getLastModifiedAt(); | ||
Line 588: | Line 580: | ||
Connector addConnectorTo(Node target); | Connector addConnectorTo(Node target); | ||
− | /** adds a new Connector object to List | + | /** adds a new Connector object to List<Node> connectors and returns |
* reference for optional further editing (style); also enlists the | * reference for optional further editing (style); also enlists the | ||
* Connector on the target Node object. */ | * Connector on the target Node object. */ | ||
Line 603: | Line 595: | ||
void delete(); | void delete(); | ||
− | /** removes connector from List | + | /** removes connector from List<Node> connectors; does the corresponding |
* on the target Node object referenced by connectorToBeRemoved */ | * on the target Node object referenced by connectorToBeRemoved */ | ||
void moveTo(Node parentNode); | void moveTo(Node parentNode); | ||
Line 616: | Line 608: | ||
/** | /** | ||
* Set the note text: | * Set the note text: | ||
− | * | + | * <ul> |
− | * | + | * <li>This methods provides automatic conversion to String in a way that node.getNote().getXyz() |
* methods will be able to convert the string properly to the wanted type. | * methods will be able to convert the string properly to the wanted type. | ||
− | * | + | * <li>Special conversion is provided for dates and calendars: They will be converted in a way that |
* node.note.date and node.note.calendar will work. All other types are converted via value.toString(). | * node.note.date and node.note.calendar will work. All other types are converted via value.toString(). | ||
− | * | + | * <li>If the conversion result is not valid HTML it will be automatically converted to HTML. |
− | * | + | * </ul> |
− | * | + | * <p> |
− | * | + | * <pre> |
* // converts numbers and other stuff with toString() | * // converts numbers and other stuff with toString() | ||
* node.note = 1.2 | * node.note = 1.2 | ||
− | * assert node.note.text == | + | * assert node.note.text == "<html><body><p>1.2" |
− | * assert node.note.plain == | + | * assert node.note.plain == "1.2" |
* assert node.note.num == 1.2d | * assert node.note.num == 1.2d | ||
* // == dates | * // == dates | ||
* // a date in some non-UTC time zone | * // a date in some non-UTC time zone | ||
− | * def date = new java.text.SimpleDateFormat( | + | * def date = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ"). |
− | * parse( | + | * parse("1970-01-01 00:00:00.000-0200") |
− | * // converts to | + | * // converts to "1970-01-01T02:00:00.000+0000" (GMT) |
* // - note the shift due to the different time zone | * // - note the shift due to the different time zone | ||
* // - the missing end tags don't matter for rendering | * // - the missing end tags don't matter for rendering | ||
* node.note = date | * node.note = date | ||
− | * assert node.note == | + | * assert node.note == "<html><body><p>1970-01-01T02:00:00.000+0000" |
− | * assert node.note.plain == | + | * assert node.note.plain == "1970-01-01T02:00:00.000+0000" |
* assert node.note.date == date | * assert node.note.date == date | ||
* // == remove note | * // == remove note | ||
* node.note = null | * node.note = null | ||
* assert node.note.text == null | * assert node.note.text == null | ||
− | * | + | * </pre> |
* @param value An object for conversion to String. Works well for all types that {@link Convertible} | * @param value An object for conversion to String. Works well for all types that {@link Convertible} | ||
* handles, particularly {@link Convertible}s itself. | * handles, particularly {@link Convertible}s itself. | ||
Line 659: | Line 651: | ||
* Special conversion is provided for dates and calendars: They will be converted in a way that | * Special conversion is provided for dates and calendars: They will be converted in a way that | ||
* node.to.date and node.to.calendar will work. All other types are converted via value.toString(): | * node.to.date and node.to.calendar will work. All other types are converted via value.toString(): | ||
− | * | + | * <pre> |
* // converts non-Dates with toString() | * // converts non-Dates with toString() | ||
* node.text = 1.2 | * node.text = 1.2 | ||
− | * assert node.to.text == | + | * assert node.to.text == "1.2" |
* assert node.to.num == 1.2d | * assert node.to.num == 1.2d | ||
* // == dates | * // == dates | ||
* // a date in some non-GMT time zone | * // a date in some non-GMT time zone | ||
− | * def date = new java.text.SimpleDateFormat( | + | * def date = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ"). |
− | * parse( | + | * parse("1970-01-01 00:00:00.000-0200") |
− | * // converts to | + | * // converts to "1970-01-01T02:00:00.000+0000" (GMT) |
* // - note the shift due to the different time zone | * // - note the shift due to the different time zone | ||
* node.text = date | * node.text = date | ||
− | * assert node.to.text == | + | * assert node.to.text == "1970-01-01T02:00:00.000+0000" |
* assert node.to.date == date | * assert node.to.date == date | ||
− | * | + | * </pre> |
* @param value A not-null object for conversion to String. Works well for all types that {@link Convertible} | * @param value A not-null object for conversion to String. Works well for all types that {@link Convertible} | ||
* handles, particularly {@link Convertible}s itself. | * handles, particularly {@link Convertible}s itself. | ||
Line 686: | Line 678: | ||
/** | /** | ||
* Allows to set and to change attribute like array elements. | * Allows to set and to change attribute like array elements. | ||
− | * | + | * <p> |
* Note that attributes are String valued. This methods provides automatic conversion to String in a way that | * Note that attributes are String valued. This methods provides automatic conversion to String in a way that | ||
− | * node[ | + | * node["a name"].getXyz() methods will be able to convert the string properly to the wanted type. |
* Special conversion is provided for dates and calendars: They will be converted in a way that | * Special conversion is provided for dates and calendars: They will be converted in a way that | ||
− | * node[ | + | * node["a name"].date and node["a name"].calendar will work. All other types are converted via |
* value.toString(): | * value.toString(): | ||
− | * | + | * <pre> |
* // == text | * // == text | ||
− | * node[ | + | * node["attribute name"] = "a value" |
− | * assert node[ | + | * assert node["attribute name"] == "a value" |
− | * assert node.attributes.getFirst( | + | * assert node.attributes.getFirst("attribute name") == "a value" // the same |
* // == numbers and others | * // == numbers and others | ||
* // converts numbers and other stuff with toString() | * // converts numbers and other stuff with toString() | ||
− | * node[ | + | * node["a number"] = 1.2 |
− | * assert node[ | + | * assert node["a number"].text == "1.2" |
− | * assert node[ | + | * assert node["a number"].num == 1.2d |
* // == dates | * // == dates | ||
* // a date in some non-GMT time zone | * // a date in some non-GMT time zone | ||
− | * def date = new java.text.SimpleDateFormat( | + | * def date = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ"). |
− | * parse( | + | * parse("1970-01-01 00:00:00.000-0200") |
− | * // converts to | + | * // converts to "1970-01-01T02:00:00.000+0000" (GMT) |
* // - note the shift due to the different time zone | * // - note the shift due to the different time zone | ||
− | * node[ | + | * node["a date"] = date |
− | * assert node[ | + | * assert node["a date"].text == "1970-01-01T02:00:00.000+0000" |
− | * assert node[ | + | * assert node["a date"].date == date |
* // == remove an attribute | * // == remove an attribute | ||
− | * node[ | + | * node["removed attribute"] = "to be removed" |
− | * assert node[ | + | * assert node["removed attribute"] == "to be removed" |
− | * node[ | + | * node["removed attribute"] = null |
− | * assert node.attributes.find( | + | * assert node.attributes.find("removed attribute") == -1 |
− | * | + | * </pre> |
* @param value An object for conversion to String. Works well for all types that {@link Convertible} | * @param value An object for conversion to String. Works well for all types that {@link Convertible} | ||
* handles, particularly {@link Convertible}s itself. Use null to unset an attribute. | * handles, particularly {@link Convertible}s itself. Use null to unset an attribute. | ||
Line 724: | Line 716: | ||
/** allows to set all attributes at once: | /** allows to set all attributes at once: | ||
− | * | + | * <pre> |
* node.attributes = [:] // clear the attributes | * node.attributes = [:] // clear the attributes | ||
* assert node.attributes.size() == 0 | * assert node.attributes.size() == 0 | ||
− | * node.attributes = [ | + | * node.attributes = ["1st" : "a value", "2nd" : "another value"] // create 2 attributes |
* assert node.attributes.size() == 2 | * assert node.attributes.size() == 2 | ||
− | * node.attributes = [ | + | * node.attributes = ["one attrib" : new Double(1.22)] // replace all attributes |
* assert node.attributes.size() == 1 | * assert node.attributes.size() == 1 | ||
− | * assert node.attributes.getFirst( | + | * assert node.attributes.getFirst("one attrib") == "1.22" // note the type conversion |
− | * assert node[ | + | * assert node["one attrib"] == "1.22" // here we compare Convertible with String |
− | * | + | * </pre> |
*/ | */ | ||
− | void setAttributes(java.util.Map | + | void setAttributes(java.util.Map<String, Object> attributes); |
} | } | ||
Line 760: | Line 752: | ||
} | } | ||
} | } | ||
− | + | </groovy> | |
[[Category:Scripting]][[Category:Advanced_Users]] | [[Category:Scripting]][[Category:Advanced_Users]] |
Revision as of 18:26, 24 November 2010
This page describes a development version which will only work with preview/alpha/beta/test versions of Freeplane. If you are seeking for the stable released version see Scripting API.
Contents
Overview over the changes
The development is currently driven by the implementation of the Formula feature that provides mindmappers with features you know from spreadsheet processors like Excel. This implies
- The syntax must allow more concise statements. For instance to convert a node text to a number you can now write to.num instead of Double.parse(node.text). Attributes are available as node['name'] in addition to the old node.attributes.get('name').
- The API must provide additional functionality that is needed for formulas.
- The API must be re-organized to provide a read-only API for formulas.
Extended Namespace for Scripts
Methods and attributes of the current node are directly available to a script so instead of node.children.size() you may simply write children.size().
Additionally the following methods are directly available:
<groovy>
/** Shortcut for node.map.node(id) - necessary for ids to other maps. */ Node N(String id); /** Shortcut for node.map.node(id).text. */ String T(String id); /** Shortcut for node.map.node(id).value. */ public Object V(String id); /** returns valueIfNull if value is null and value otherwise. */ public Object ifNull(Object value, Object valueIfNull);
</groovy>
Convertible
A very important usability improvement, not only for formula writers, is the introduction of the class Convertible that is returned now by some new methods/properties:
- node.to (or node.getTo())
- node['attr_name'] (or node.getAt('attr_name'))
- node.note (or node.getNote())
Extended Setters
Many "setters" on the other hand, like node.setText() have been extended to accept not only Strings but Objects. Much effort was spent to ensure that this conversion matches the conversions that Convertible performs for Strings. For example node.text = new Date() is converted to 2010-10-05T22:11:03.243+0000 which Convertible knows how to convert back to date (try node.to.date).
For formulas it's important that the formulas itself don't change the state of the map. Currently only the first step is made: All subinterfaces Xyz have a base interface XyzRO that includes only the methods that are suitable for formulas. The Proxy implementations implement the full interfaces currently and the constraint is not enforced.
New Free Functions
New Controller methods
Some controller methods were introduced mainly for testing:
- Controller.newMap()
- Controller.undo()
- Controller.redo()
Example Maps
- Scripting API improvements (download only since the browser doesn't support formulas yet)
Entry Points
Each script is given two variables:
<groovy> final Proxy.Node node; final Proxy.Controller c; </groovy>
New: Methods and properties of the current node are directly available so you can write children.size() instead of node.children.size().
Interface
<groovy> package org.freeplane.plugin.script.proxy;
import groovy.lang.Closure;
import java.awt.Color; import java.io.File; import java.net.URL; import java.util.Collection; import java.util.Date; import java.util.List;
import javax.swing.Icon;
import org.freeplane.core.util.FreeplaneIconUtils; import org.freeplane.core.util.FreeplaneVersion; import org.freeplane.features.common.edge.EdgeStyle; import org.freeplane.features.common.filter.condition.ICondition; import org.freeplane.features.common.link.ArrowType; import org.freeplane.features.common.styles.IStyle;
public interface Proxy {
interface AttributesRO { /** alias for getFirst(int). * @deprecated before 1.1 - use get(int), getFirst(int) or getAll(String) instead. */ @Deprecated String get(final String name);
/** returns the first value of an attribute with the given name or null otherwise. @since 1.2 */ String getFirst(final String name);
/** returns all values for the attribute name. */ List<String> getAll(final String name);
/** returns all attribute names in the proper sequence. The number of names returned * is equal to the number of attributes.
*
* // rename attribute * int i = 0; * for (String name : attributes.getAttributeNames()) { * if (name.equals("xy")) * attributes.set(i, "xyz", attributes.get(i)); * ++i; * } *
*/
List<String> getAttributeNames();
/** returns the attribute value at the given index. * @throws IndexOutOfBoundsException if index is out of range (index * < 0 || index >= size()).*/ String get(final int index);
/** @deprecated since 1.2 - use findFirst(String) instead. */ int findAttribute(final String name);
/** returns the index of the first attribute with the given name if one exists or -1 otherwise. * For searches for all attributes with a given namegetAttributeNames()
* must be used. @since 1.2*/ int findFirst(final String name); /** the number of attributes. It issize() == getAttributeNames().size()
. */ int size(); }
/** Attributes are name - value pairs assigned to a node. A node may have multiple attributes * with the same name. */ interface Attributes extends AttributesRO { /** sets the value of the attribute at an index. This method will not create new attributes. * @throws IndexOutOfBoundsException if index is out of range (index * < 0 || index >= size()). */ void set(final int index, final String value);
/** sets name and value of the attribute at the given index. This method will not create new attributes. * @throws IndexOutOfBoundsException if index is out of range (index * < 0 || index >= size()). */ void set(final int index, final String name, final String value);
/** removes the first attribute with this name. * @returns true on removal of an existing attribute and false otherwise. * @deprecated before 1.1 - use remove(int) or removeAll(String) instead. */ @Deprecated boolean remove(final String name);
/** removes all attributes with this name. * @returns true on removal of an existing attribute and false otherwise. */ boolean removeAll(final String name);
/** removes the attribute at the given index. * @throws IndexOutOfBoundsException if index is out of range (index * < 0 || index >= size()). */ void remove(final int index);
/** adds an attribute if there is no attribute with the given name or changes * the value of the first attribute with the given name. */ void set(final String name, final String value);
/** adds an attribute no matter if an attribute with the given name already exists. */ void add(final String name, final String value);
/** removes all attributes. @since 1.2 */ void clear(); }
interface ConnectorRO { Color getColor();
ArrowType getEndArrow();
String getMiddleLabel();
Node getSource();
String getSourceLabel();
ArrowType getStartArrow();
Node getTarget();
String getTargetLabel();
boolean simulatesEdge(); }
interface Connector extends ConnectorRO { void setColor(Color color);
void setEndArrow(ArrowType arrowType);
void setMiddleLabel(String label);
void setSimulatesEdge(boolean simulatesEdge);
void setSourceLabel(String label);
void setStartArrow(ArrowType arrowType);
void setTargetLabel(String label); }
interface ControllerRO { /** if multiple nodes are selected returns one (arbitrarily chosen) * selected node or the selected node for a single node selection. */ Node getSelected();
List<Node> getSelecteds();
/** returns List<Node> of Node objects sorted on Y * * @param differentSubtrees if true * children/grandchildren/grandgrandchildren/... nodes of selected * parent nodes are excluded from the result. */ List<Node> getSortedSelection(boolean differentSubtrees);
/** * returns Freeplane version. * Use it like this:
*
* import org.freeplane.core.util.FreeplaneVersion * import org.freeplane.core.ui.components.UITools * * def required = FreeplaneVersion.getVersion("1.1.2"); * if (c.freeplaneVersion < required) * UITools.errorMessage("Freeplane version " + c.freeplaneVersion * + " not supported - update to at least " + required); *
*/ FreeplaneVersion getFreeplaneVersion();
/** Starting from the root node, recursively searches for nodes for which
* condition.checkNode(node)
returns true.
* @see Node.find(ICondition) for searches on subtrees
* @deprecated since 1.2 use find(Closure) instead. */
List<Node> find(ICondition condition);
/**
* Starting from the root node, recursively searches for nodes for which closure.call(node)
* returns true.
*
* A find method that uses a Groovy closure ("block") for simple custom searches. As this closure
* will be called with a node as an argument (to be referenced by it
) the search can
* evaluate every node property, like attributes, icons, node text or notes.
*
* Examples: *
* def nodesWithNotes = c.find{ it.noteText != null } * * def matchingNodes = c.find{ it.text.matches(".*\\d.*") } * def texts = matchingNodes.collect{ it.text } * print "node texts containing numbers:\n " + texts.join("\n ") *
* @param closure a Groovy closure that returns a boolean value. The closure will receive
* a NodeModel as an argument which can be tested for a match.
* @return all nodes for which closure.call(NodeModel)
returns true.
* @see Node.find(Closure) for searches on subtrees
*/
List<Node> find(Closure closure);
}
interface Controller extends ControllerRO { void centerOnNode(Node center);
void select(Node toSelect);
/** selects branchRoot and all children */ void selectBranch(Node branchRoot);
/** toSelect is a List<Node> of Node objects */ void selectMultipleNodes(List<Node> toSelect);
/** reset undo / redo lists and deactivate Undo for current script */ void deactivateUndo();
/** invokes undo once - for testing purposes mainly. @since 1.2 */ void undo();
/** invokes redo once - for testing purposes mainly. @since 1.2 */ void redo();
/** The main info for the status line with key="standard", use null to remove. Removes icon if there is one. */ void setStatusInfo(String info);
/** Info for status line, null to remove. Removes icon if there is one. * @see setStatusInfo(String, String, String) */ void setStatusInfo(String infoPanelKey, String info);
/** Info for status line - text and icon - null stands for "remove" (text or icon) * @param infoPanelKey "standard" is the left most standard info panel. If a panel with * this name doesn't exist it will be created. * @param info Info text * @param iconKey key as those that are used for nodes (see {@link Icons#addIcon(String)}).
*
* println("all available icon keys: " + FreeplaneIconUtils.listStandardIconKeys()) * c.setStatusInfo("standard", "hi there!", "button_ok"); *
* @see FreeplaneIconUtils * @since 1.2 */ void setStatusInfo(String infoPanelKey, String info, String iconKey); /** @deprecated since 1.2 - use setStatusInfo(String, String, String) */ void setStatusInfo(String infoPanelKey, Icon icon);
/** opens a new map with a default name in the foreground. @since 1.2 */ Map newMap();
/** opens a new map for url in the foreground if it isn't opened already. @since 1.2 */ Map newMap(URL url); }
interface EdgeRO { Color getColor();
EdgeStyle getType();
int getWidth(); }
interface Edge extends EdgeRO { void setColor(Color color);
void setType(EdgeStyle type);
/** can be -1 for default, 0 for thin, >0 */ void setWidth(int width); }
interface ExternalObjectRO { /** empty string means that there's no external object */ String getURI();
float getZoom(); }
interface ExternalObject extends ExternalObjectRO { /** setting empty String uri means remove external object (as for Links); */ void setURI(String uri);
void setZoom(float zoom); }
interface FontRO { String getName();
int getSize();
boolean isBold();
boolean isBoldSet();
boolean isItalic();
boolean isItalicSet();
boolean isNameSet();
boolean isSizeSet(); }
interface Font extends FontRO { void resetBold();
void resetItalic();
void resetName();
void resetSize();
void setBold(boolean bold);
void setItalic(boolean italic);
void setName(String name);
void setSize(int size); }
interface IconsRO { /** returns List<Node> of Strings (corresponding to iconID above); * iconID is one of "Idea","Question","Important", etc. */ List<String> getIcons(); }
interface Icons extends IconsRO { /** * adds an icon to a node if an icon for the given key can be found. The same icon can be added multiple * times.
*
* println("all available icon keys: " + FreeplaneIconUtils.listStandardIconKeys()) * node.icons.addIcon("button_ok") *
* @see FreeplaneIconUtils */ void addIcon(String name);
/** deletes first occurence of icon with the given name, returns true if * success (icon existed); */ boolean removeIcon(String name); }
interface LinkRO { String get(); }
interface Link extends LinkRO { /** target is a URI. * An empty String will remove the link. * To get a local link (i.e. to another node) target should be: "#" + nodeId */ boolean set(String target); }
interface MapRO { /** @since 1.2 */ Node getRoot();
/** @deprecated since 1.2 - use getRoot() instead. */ Node getRootNode();
/** returns the node if the map contains it or null otherwise. */ Node node(String id);
/** returns the physical location of the map if available or null otherwise. */ File getFile();
/** returns the title of the MapView. @since 1.2 */ String getName(); }
interface Map extends MapRO { /** * closes a map. Note that there is no undo for this method. * @param close map even if there are unsaved changes. * @param allowInteraction if (allowInteraction && ! force) a saveAs dialog will be opened if there are * unsaved changes. * @return false if the saveAs was cancelled by the user and true otherwise. * @throws RuntimeException if the map contains changes and parameter force is false. * @since 1.2 */ boolean close(boolean force, boolean allowInteraction);
/** * saves the map to disk. Note that there is no undo for this method. * @param allowInteraction if a saveAs dialog should be opened if the map has no assigned URL so far. * @return false if the saveAs was cancelled by the user and true otherwise. * @throws RuntimeException if the map has no assigned URL and parameter allowInteraction is false. * @since 1.2 */ boolean save(boolean allowInteraction); }
interface NodeRO { Attributes getAttributes();
/** allows to access attribute values like array elements. Note that the returned type is a * {@link Convertible}, not a String. Nevertheless it behaves like a String in almost all respects, * that is, in Groovy scripts it understands all String methods like lenght(), matches() etc.
*
* // standard way * node.attributes.set("attribute name", "12") * // implicitely use getAt() * def val = node["attribute name"] * // use all conversions that Convertible provides (num, date, string, ...) * assert val.num == new Long(12) * // or use it just like a string * assert val.startsWith("1") *
* @since 1.2 */ Convertible getAt(String attributeName);
/** returns the index (0..) of this node in the (by Y coordinate sorted) * list of this node's children. Returns -1 if childNode is not a child * of this node. */ int getChildPosition(Node childNode);
/** returns the children of this node ordered by Y coordinate. */ List<Node> getChildren();
Collection<Connector> getConnectorsIn();
Collection<Connector> getConnectorsOut();
ExternalObject getExternalObject();
Icons getIcons();
Link getLink();
/** the map this node belongs to. */ Map getMap();
/** @deprecated since 1.2 - use Node.getId() instead. */ String getNodeID();
/** @since 1.2 */ String getId();
/** if countHidden is false then only nodes that are matched by the * current filter are counted. */ int getNodeLevel(boolean countHidden);
/** * Returns a Convertible object for the plain not text. Convertibles behave like Strings in most respects. * Additionally String methods are overridden to handle Convertible arguments as if the argument were the * result of Convertible.getText(). * @return Convertible getString(), getText() and toString() will return plain text instead of the HTML. * Use getNoteText() to get the HTML text. * @since 1.2 */ Convertible getNote(); /** Returns the HTML text of the node. (Notes always contain HTML text.) */ String getNoteText();
/** @since 1.2 */ Node getParent();
/** @deprecated since 1.2 - use getParent() instead. */ Node getParentNode();
NodeStyle getStyle();
/** use this method to remove all tags from an HTML node. @since 1.2 */ String getPlainText();
/** use this method to remove all tags from an HTML node. * @deprecated since 1.2 - use getPlainText() or getTo().getPlain() instead. */ String getPlainTextContent();
String getText();
/** * returns an object that performs conversions (method name is choosen to give descriptive code):
*
-
*
- node.to.num
- Long or Double, see {@link Convertible#getDate()}. *
- node.to.date
- Date, see {@link Convertible#getDate()}. *
- node.to.string
- Text, see {@link Convertible#getString()}. *
- node.to.text
- an alias for getString(), see {@link Convertible#getText()}. *
- node.to.object
- returns what fits best, see {@link Convertible#getObject()}. *
* Note that parse errors result in {@link ConversionException}s. * @return ConvertibleObject * @since 1.2 */ Convertible getTo();
/** an alias for getTo(). @since 1.2 */ Convertible getValue();
/** returns true if p is a parent, or grandparent, ... of this node, or if it is equal * to this node; returns false otherwise. */ boolean isDescendantOf(Node p);
boolean isFolded();
boolean isLeaf();
boolean isLeft();
boolean isRoot();
boolean isVisible();
/** Starting from this node, recursively searches for nodes for which
* condition.checkNode(node)
returns true.
* @deprecated since 1.2 use find(Closure) instead. */
List<Node> find(ICondition condition);
/** Starting from this node, recursively searches for nodes for which closure.call(node)
* returns true. See {@link Controller#find(Closure)} for details. */
List<Node> find(Closure closure);
Date getLastModifiedAt();
Date getCreatedAt(); }
interface Node extends NodeRO { Connector addConnectorTo(Node target);
/** adds a new Connector object to List<Node> connectors and returns * reference for optional further editing (style); also enlists the * Connector on the target Node object. */ Connector addConnectorTo(String targetNodeId);
/** inserts *new* node as child, takes care of all construction work and * internal stuff inserts as last child. */ Node createChild();
/** inserts *new* node as child, takes care of all construction work and * internal stuff */ Node createChild(int position);
void delete();
/** removes connector from List<Node> connectors; does the corresponding * on the target Node object referenced by connectorToBeRemoved */ void moveTo(Node parentNode);
void moveTo(Node parentNode, int position);
/** as above, using String nodeId instead of Node object to establish the connector*/ void removeConnector(Connector connectorToBeRemoved);
void setFolded(boolean folded);
/** * Set the note text:
*
-
*
- This methods provides automatic conversion to String in a way that node.getNote().getXyz() * methods will be able to convert the string properly to the wanted type. *
- Special conversion is provided for dates and calendars: They will be converted in a way that * node.note.date and node.note.calendar will work. All other types are converted via value.toString(). *
- If the conversion result is not valid HTML it will be automatically converted to HTML. *
*
*
* // converts numbers and other stuff with toString() * node.note = 1.2 * assert node.note.text == "<html><body><p>1.2" * assert node.note.plain == "1.2" * assert node.note.num == 1.2d * // == dates * // a date in some non-UTC time zone * def date = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ"). * parse("1970-01-01 00:00:00.000-0200") * // converts to "1970-01-01T02:00:00.000+0000" (GMT) * // - note the shift due to the different time zone * // - the missing end tags don't matter for rendering * node.note = date * assert node.note == "<html><body><p>1970-01-01T02:00:00.000+0000" * assert node.note.plain == "1970-01-01T02:00:00.000+0000" * assert node.note.date == date * // == remove note * node.note = null * assert node.note.text == null *
* @param value An object for conversion to String. Works well for all types that {@link Convertible} * handles, particularly {@link Convertible}s itself. * @since 1.2 (note that the old setNoteText() did not support non-String arguments. */ void setNote(Object value);
/** @deprecated since 1.2 - use setNote() instead. */ void setNoteText(String text);
/** * A node's text is String valued. This methods provides automatic conversion to String in a way that * node.to.getXyz() methods will be able to convert the string properly to the wanted type. * Special conversion is provided for dates and calendars: They will be converted in a way that * node.to.date and node.to.calendar will work. All other types are converted via value.toString():
*
* // converts non-Dates with toString() * node.text = 1.2 * assert node.to.text == "1.2" * assert node.to.num == 1.2d * // == dates * // a date in some non-GMT time zone * def date = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ"). * parse("1970-01-01 00:00:00.000-0200") * // converts to "1970-01-01T02:00:00.000+0000" (GMT) * // - note the shift due to the different time zone * node.text = date * assert node.to.text == "1970-01-01T02:00:00.000+0000" * assert node.to.date == date *
* @param value A not-null object for conversion to String. Works well for all types that {@link Convertible} * handles, particularly {@link Convertible}s itself. */ void setText(Object value);
void setLastModifiedAt(Date date);
void setCreatedAt(Date date);
// Attributes /** * Allows to set and to change attribute like array elements.
*
* Note that attributes are String valued. This methods provides automatic conversion to String in a way that * node["a name"].getXyz() methods will be able to convert the string properly to the wanted type. * Special conversion is provided for dates and calendars: They will be converted in a way that * node["a name"].date and node["a name"].calendar will work. All other types are converted via * value.toString(): *
* // == text * node["attribute name"] = "a value" * assert node["attribute name"] == "a value" * assert node.attributes.getFirst("attribute name") == "a value" // the same * // == numbers and others * // converts numbers and other stuff with toString() * node["a number"] = 1.2 * assert node["a number"].text == "1.2" * assert node["a number"].num == 1.2d * // == dates * // a date in some non-GMT time zone * def date = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ"). * parse("1970-01-01 00:00:00.000-0200") * // converts to "1970-01-01T02:00:00.000+0000" (GMT) * // - note the shift due to the different time zone * node["a date"] = date * assert node["a date"].text == "1970-01-01T02:00:00.000+0000" * assert node["a date"].date == date * // == remove an attribute * node["removed attribute"] = "to be removed" * assert node["removed attribute"] == "to be removed" * node["removed attribute"] = null * assert node.attributes.find("removed attribute") == -1 *
* @param value An object for conversion to String. Works well for all types that {@link Convertible} * handles, particularly {@link Convertible}s itself. Use null to unset an attribute. * @return the new value */ String putAt(String attributeName, Object value);
/** allows to set all attributes at once:
*
* node.attributes = [:] // clear the attributes * assert node.attributes.size() == 0 * node.attributes = ["1st" : "a value", "2nd" : "another value"] // create 2 attributes * assert node.attributes.size() == 2 * node.attributes = ["one attrib" : new Double(1.22)] // replace all attributes * assert node.attributes.size() == 1 * assert node.attributes.getFirst("one attrib") == "1.22" // note the type conversion * assert node["one attrib"] == "1.22" // here we compare Convertible with String *
*/ void setAttributes(java.util.Map<String, Object> attributes); }
interface NodeStyleRO { IStyle getStyle();
Node getStyleNode();
Color getBackgroundColor();
Edge getEdge();
Font getFont();
Color getNodeTextColor(); }
interface NodeStyle extends NodeStyleRO { void setStyle(IStyle key);
void setBackgroundColor(Color color);
void setNodeTextColor(Color color); }
} </groovy>