Repository navigation
Expand file tree
/
Copy pathNodeLibrary.java
More file actions
334 lines (290 loc) · 11.3 KB
/
Copy pathNodeLibrary.java
File metadata and controls
334 lines (290 loc) · 11.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
package nodebox.node;
import nodebox.util.FileUtils;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
/**
* A Node library stores a set of (possibly hierarchical) nodes.
* <p/>
* Node libraries are both documents and libraries. By mentioning a node on the library search path, you can get
* all the nodes.
* <p/>
* Node libraries can be backed by files, and saved in that same file, or they can be stored in memory only.
* <p/>
* This implementation of the node library only stores a root node.
* Calling get(String) on the library actually forwards the call to getChild() on the root node.
*/
public class NodeLibrary {
public static final NodeLibrary BUILTINS = new NodeLibrary();
private String name;
private File file;
private Node rootNode;
private HashMap<String, String> variables;
private NodeCode code;
private DependencyGraph<Parameter, Object> parameterGraph = new DependencyGraph<Parameter, Object>();
/**
* Load a library from the given XML.
* <p/>
* This library is not added to the manager. The manager is used only to look up prototypes.
* You can add the library to the manager yourself using manager.add(), or by calling
* manager.load().
*
* @param libraryName the name of the new library
* @param xml the xml data of the library
* @param manager the manager used to look up node prototypes.
* @return a new node library
* @throws RuntimeException When the string could not be parsed.
* @see nodebox.node.NodeLibraryManager#add(NodeLibrary)
* @see nodebox.node.NodeLibraryManager#load(String, String)
*/
public static NodeLibrary load(String libraryName, String xml, NodeLibraryManager manager) throws RuntimeException {
try {
NodeLibrary library = new NodeLibrary(libraryName);
load(library, new ByteArrayInputStream(xml.getBytes("UTF8")), manager);
return library;
} catch (ParserConfigurationException e) {
throw new RuntimeException("Error in the XML parser configuration", e);
} catch (SAXException e) {
throw new RuntimeException("Error while parsing: " + e.getMessage(), e);
} catch (IOException e) {
throw new RuntimeException("I/O error while parsing.", e);
}
}
/**
* Load a library from the given file.
* <p/>
* This library is not added to the manager. The manager is used only to look up prototypes.
* You can add the library to the manager yourself using manager.add(), or by calling
* manager.load().
*
* @param f the file to load
* @param manager the manager used to look up node prototypes.
* @return a new node library
* @throws RuntimeException When the file could not be found, or parsing failed.
* @see nodebox.node.NodeLibraryManager#add(NodeLibrary)
* @see nodebox.node.NodeLibraryManager#load(File)
*/
public static NodeLibrary load(File f, NodeLibraryManager manager) throws RuntimeException {
try {
// The library name is the file name without the ".ndbx" extension.
// Chop off the .ndbx
String libraryName = FileUtils.stripExtension(f);
NodeLibrary library = new NodeLibrary(libraryName, f);
load(library, new FileInputStream(f), manager);
return library;
} catch (ParserConfigurationException e) {
throw new RuntimeException("Error in the XML parser configuration", e);
} catch (SAXException e) {
throw new RuntimeException("Error while parsing: " + e.getMessage(), e);
} catch (FileNotFoundException e) {
throw new RuntimeException("File not found " + f, e);
} catch (IOException e) {
throw new RuntimeException("I/O error while parsing " + f, e);
}
}
/**
* This method gets called from the public load method and does the actual parsing.
* <p/>
* The method requires a newly created (empty) library. Nodes are added to this library.
*
* @param library the newly created library
* @param is the input stream data
* @param manager the manager used for looking up prototypes.
* @throws IOException when the data could not be loaded
* @throws ParserConfigurationException when the parser is incorrectly configured
* @throws SAXException when the data could not be parsed
*/
private static void load(NodeLibrary library, InputStream is, NodeLibraryManager manager) throws IOException, ParserConfigurationException, SAXException {
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser parser = spf.newSAXParser();
NDBXHandler handler = new NDBXHandler(library, manager);
parser.parse(is, handler);
}
private NodeLibrary() {
this.name = "builtins";
this.file = null;
this.rootNode = null;
this.variables = null;
}
public NodeLibrary(String name) {
this(name, null);
}
public NodeLibrary(String name, File file) {
this.name = name;
this.file = file;
this.rootNode = Node.ROOT_NODE.newInstance(this, "root");
this.variables = new HashMap<String, String>();
}
public String getName() {
return name;
}
public File getFile() {
return file;
}
//// Node management ////
public Node getRootNode() {
return rootNode;
}
public List<Node> getExportedNodes() {
List<Node> allChildren = rootNode.getChildren();
List<Node> exportedChildren = new ArrayList<Node>(allChildren.size());
for (Node child : allChildren) {
if (child.isExported()) {
exportedChildren.add(child);
}
}
return exportedChildren;
}
public void add(Node node) {
if (node.getLibrary() != this) throw new AssertionError("This node is already added to another library.");
// The root node can be null in only one case: when we're creating the builtins library.
// In that case, the rootNode becomes the given node.
if (rootNode == null) {
rootNode = node;
} else {
rootNode.add(node);
}
}
/**
* Get a node from this library.
* <p/>
* Only exported nodes are returned. If you want all nodes, use getRootNode().getChild()
*
* @param name the name of the node
* @return the node, or null if a node with this name could not be found.
*/
public Node get(String name) {
if ("root".equals(name)) return rootNode;
return rootNode.getExportedChild(name);
}
public Node remove(String name) {
Node node = rootNode.getChild(name);
if (node == null) return null;
rootNode.remove(node);
return node;
}
public boolean remove(Node node) {
return rootNode.remove(node);
}
public int size() {
return rootNode.size();
}
public boolean contains(String nodeName) {
return rootNode.contains(nodeName);
}
//// Variables ////
public String[] getVariableNames() {
return variables.keySet().toArray(new String[variables.keySet().size()]);
}
public String getVariable(String name) {
return variables.get(name);
}
public void setVariable(String name, String value) {
variables.put(name, value);
}
//// Code ////
public void setCode(NodeCode code) {
this.code = code;
}
public NodeCode getCode() {
return code;
}
//// Persistence /////
public void store() throws IOException, IllegalArgumentException {
if (file == null)
throw new IllegalArgumentException("Library was not loaded from a file and no file given to store.");
store(file);
}
public void store(File f) throws IOException {
NDBXWriter.write(this, f);
}
/**
* Get the full XML data for this library and all of its nodes.
*
* @return an XML string
*/
public String toXml() {
return NDBXWriter.asString(this);
}
//// Parameter dependencies ////
/**
* Add a dependency between two parameters.
* <p/>
* Whenever the dependent node wants to update, it needs to check if the dependency
* is clean. Also, whenever the dependency changes, the dependent gets notified.
* <p/>
* Do not call this method directly. Instead, let Parameter create the dependencies by using setExpression().
*
* @param dependency the parameter that provides the value
* @param dependent the parameter that needs the value
* @see nodebox.node.Parameter#setExpression(String)
*/
public void addParameterDependency(Parameter dependency, Parameter dependent) {
parameterGraph.addDependency(dependency, dependent);
}
/**
* Remove all dependencies this parameter has.
* <p/>
* This method gets called when a parameter clears out its expression.
* <p/>
* Do not call this method directly. Instead, let Parameter remove dependencies by using clearExpression().
*
* @param p the parameter
* @see nodebox.node.Parameter#clearExpression()
*/
public void removeParameterDependencies(Parameter p) {
parameterGraph.removeDependencies(p);
}
/**
* Remove all dependents this parameter has.
* <p/>
* This method gets called when the parameter is about to be removed. It signal all of its dependent nodes
* that the parameter will no longer be available.
* <p/>
* Do not call this method directly. Instead, let Parameter remove dependents by using removeParameter().
*
* @param p the parameter
* @see Node#removeParameter(String)
*/
public void removeParameterDependents(Parameter p) {
parameterGraph.removeDependents(p);
}
/**
* Get all parameters that rely on this parameter.
* <p/>
* These parameters all have expressions that point to this parameter. Whenever this parameter changes,
* they get notified.
* <p/>
* This list contains all "live" parameters when you call it. Please don't hold on to this list for too long,
* since parameters can be added and removed at will.
*
* @param p the parameter
* @return a list of parameters that depend on this parameter. This list can safely be modified.
*/
public Set<Parameter> getParameterDependents(Parameter p) {
return parameterGraph.getDependents(p);
}
/**
* Get all parameters this parameter depends on.
* <p/>
* This list contains all "live" parameters when you call it. Please don't hold on to this list for too long,
* since parameters can be added and removed at will.
*
* @param p the parameter
* @return a list of parameters this parameter depends on. This list can safely be modified.
*/
public Set<Parameter> getParameterDependencies(Parameter p) {
return parameterGraph.getDependencies(p);
}
//// Standard overrides ////
@Override
public String toString() {
return getName();
}
}