001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010: package org.mmbase.util.functions;
011:
012: import java.util.*;
013: import org.mmbase.module.core.MMObjectNode;
014: import org.mmbase.module.core.MMObjectBuilder;
015: import org.mmbase.bridge.*;
016:
017: import org.mmbase.util.logging.Logger;
018: import org.mmbase.util.logging.Logging;
019:
020: /**
021: * A NodeFunction represents a function on a node instances of this builder. This means
022: * that it always has one implicit node argument. This node-argument needs not be mentioned in
023: * the Parameter array of the constructor.
024: *
025: * @author Michiel Meeuwissen
026: * @version $Id: NodeFunction.java,v 1.31 2007/11/25 18:25:49 nklasens Exp $
027: * @see org.mmbase.module.core.MMObjectBuilder#executeFunction
028: * @see org.mmbase.bridge.Node#getFunctionValue
029: * @see org.mmbase.util.functions.BeanFunction
030: * @since MMBase-1.8
031: */
032:
033: public abstract class NodeFunction<R> extends AbstractFunction<R> {
034:
035: private static final Logger log = Logging
036: .getLoggerInstance(NodeFunction.class);
037:
038: /**
039: * @return The currently set ReturnType, or <code>null</code> if not set already.
040: */
041: public ReturnType<R> getReturnType() {
042: if (returnType == null && autoReturnType) {
043: try {
044: returnType = (ReturnType<R>) ReturnType
045: .getReturnType(getClass().getDeclaredMethod(
046: "getFunctionValue", Node.class,
047: Parameters.class).getReturnType());
048: } catch (Exception e) {
049: log.error(e.getMessage(), e);
050: }
051: }
052: return returnType;
053: }
054:
055: /**
056: * Utility function, for easy call of function on node by one string.
057: */
058: public static FieldValue getFunctionValue(Node node, String function) {
059: if (node == null) {
060: log.warn("Tried to execute node-function on null!");
061: return null;
062: }
063: List<?> args = new ArrayList();
064: String functionName = getFunctionNameAndFillArgs(function, args);
065: if (log.isDebugEnabled()) {
066: log.debug("Executing " + functionName + " " + args + " on "
067: + node.getNumber());
068: }
069:
070: return node.getFunctionValue(functionName, args);
071: }
072:
073: public static String getFunctionNameAndFillArgs(String function,
074: java.util.List args) {
075: String functionName = function;
076: int pos1 = function.indexOf('(');
077: if (pos1 != -1) {
078: int pos2 = function.lastIndexOf(')');
079: if (pos2 != -1) {
080: functionName = function.substring(0, pos1);
081: java.util.List args2 = org.mmbase.util.StringSplitter
082: .splitFunctions(function.subSequence(pos1 + 1,
083: pos2));
084: args.addAll(args2);
085: }
086: }
087: return functionName;
088: }
089:
090: public NodeFunction(String name, Parameter<?>[] def,
091: ReturnType<R> returnType) {
092: super (name, getNodeParameterDef(def), returnType);
093: }
094:
095: /**
096: * @since MMBase-1.9
097: */
098: public NodeFunction(String name, Parameter... def) {
099: super (name, getNodeParameterDef(def));
100: }
101:
102: protected static Parameter[] getNodeParameterDef(Parameter... def) {
103: List<Parameter> defList = new ArrayList(Arrays.asList(def));
104: if (!defList.contains(Parameter.NODE))
105: defList.add(Parameter.NODE);
106: if (!defList.contains(Parameter.CLOUD))
107: defList.add(Parameter.CLOUD);
108: if (!defList.contains(Parameter.CORENODE))
109: defList.add(Parameter.CORENODE);
110: return defList.toArray(Parameter.emptyArray());
111: }
112:
113: /**
114: * Returns a new instance of NodeInstanceFunction, which represents an actual Function.
115: */
116: final public Function<R> newInstance(MMObjectNode node) {
117: return new NodeInstanceFunction(node);
118: }
119:
120: /**
121: * Implements the function on a certain node. Override this method <em>or</em> it's bridge
122: * counter-part {@link #getFunctionValue(org.mmbase.bridge.Node, Parameters)}. Overriding the
123: * bridge version has two advantages. It's easier, and mmbase security will be honoured. That
124: * last thing is of course not necesary if you are not going to use other nodes.
125: *
126: * XXX: made final because it does not work well if you don't implement a bridge version
127: */
128: protected final R getFunctionValue(final MMObjectNode coreNode,
129: final Parameters parameters) {
130: if (coreNode == null)
131: throw new RuntimeException("No node argument given for "
132: + this + "(" + parameters + ")!");
133: Node node = parameters.get(Parameter.NODE);
134: if (node == null) {
135: Cloud cloud = parameters.get(Parameter.CLOUD);
136: if (cloud == null) {
137: // lets try this
138: try {
139: cloud = org.mmbase.bridge.ContextProvider
140: .getDefaultCloudContext().getCloud(
141: "mmbase", "class", null);
142: } catch (org.mmbase.security.SecurityException se) {
143: // perhaps class-security not implemented by security implementation.
144: log.warn("" + se.getMessage());
145: cloud = org.mmbase.bridge.ContextProvider
146: .getDefaultCloudContext()
147: .getCloud("mmbase");
148: }
149: if (cloud == null) {
150: throw new RuntimeException(
151: "No cloud argument given" + this + "("
152: + parameters + ")!"
153: + Logging.stackTrace());
154: }
155: }
156: if (coreNode instanceof org.mmbase.module.core.VirtualNode) {
157: node = new org.mmbase.bridge.implementation.VirtualNode(
158: (org.mmbase.module.core.VirtualNode) coreNode,
159: cloud);
160: } else {
161: int number = coreNode.getNumber();
162: if (number == -1) {
163: // must be in transaction or uncommited node
164: String tmpNumber = coreNode
165: .getStringValue(MMObjectBuilder.TMP_FIELD_NUMBER);
166: if (cloud.hasNode(tmpNumber)) {
167: node = cloud.getNode(tmpNumber);
168: } else {
169: // last resort..., we're really desperate now.
170: // This happens when calling gui() in transaction.
171: // Perhaps we need something like a public new BasicNode(MMobjectNode, Cloud). Abusing VirtualNode for similar purpose now.
172: org.mmbase.module.core.VirtualNode virtual = new org.mmbase.module.core.VirtualNode(
173: coreNode.getBuilder());
174: Iterator<Map.Entry<String, Object>> i = coreNode
175: .getValues().entrySet().iterator();
176: while (i.hasNext()) {
177: Map.Entry<String, Object> entry = i.next();
178: virtual.storeValue(entry.getKey(), entry
179: .getValue());
180: }
181: node = new org.mmbase.bridge.implementation.VirtualNode(
182: virtual, cloud);
183: }
184: } else {
185: if (cloud.mayRead(number)) {
186: node = cloud.getNode(number);
187: } else {
188: log.warn("Could not produce Bridge Node for '"
189: + number
190: + "', cannot execute node function.");
191: return null;
192: }
193: }
194: }
195: parameters.set(Parameter.NODE, node);
196: }
197: return getFunctionValue(node, parameters);
198:
199: }
200:
201: /**
202: * Utility method to convert a {@link org.mmbase.bridge.Node} to a a {@link org.mmbase.module.core.MMObjectNode}.
203: */
204: protected final MMObjectNode getCoreNode(
205: final MMObjectBuilder builder, final Node node) {
206: if (node instanceof org.mmbase.bridge.implementation.VirtualNode) {
207: return ((org.mmbase.bridge.implementation.VirtualNode) node)
208: .getNodeRef();
209: } else {
210: return builder.getNode(node.getNumber());
211: }
212:
213: }
214:
215: /**
216: */
217: protected abstract R getFunctionValue(Node node,
218: Parameters parameters);
219:
220: protected Node getNode(Parameters parameters) {
221: if (!parameters.containsParameter(Parameter.NODE)) {
222: throw new IllegalArgumentException("The function "
223: + toString() + " requires a node argument");
224: }
225: Node node = parameters.get(Parameter.NODE);
226: if (node == null) {
227: throw new IllegalArgumentException("The '" + Parameter.NODE
228: + "' argument of " + getClass() + " " + toString()
229: + " must not be null ");
230: }
231: return node;
232: }
233:
234: /**
235: * To implement a NodeFunction, you must override {@link #getFunctionValue(Node, Parameters)}.
236: * This one can be overriden if the same function must <em>also</em> be a builder function.
237: */
238: public R getFunctionValue(Parameters parameters) {
239: return getFunctionValue(getNode(parameters), parameters);
240: }
241:
242: /**
243: * Tries to convert a certain Function object into a NodeFunction object.
244: * @return <code>function</code> if that was already a NodeFunction, <code>null</code> if it
245: * could not be wrapped (No {@link Parameter#NODE} parameter), or a new NodeFunction object
246: * wrapping <code>function</code>
247: *
248: * @since MMBase-1.8.5
249: */
250: public static <S> NodeFunction<S> wrap(Function<S> function) {
251: if (function instanceof NodeFunction) {
252: return (NodeFunction<S>) function;
253: } else {
254: // if it contains a 'node' parameter, it can be wrapped into a node-function,
255: // and be available on nodes of this builder.
256: Parameters test = function.createParameters();
257: if (test.containsParameter(Parameter.NODE)) {
258: final Function<S> f = function;
259: return new NodeFunction<S>(function.getName(), function
260: .getParameterDefinition(), function
261: .getReturnType()) {
262: protected S getFunctionValue(
263: org.mmbase.bridge.Node node,
264: Parameters parameters) {
265: if (parameters == null)
266: parameters = createParameters();
267: parameters.set(Parameter.NODE, node);
268: return f.getFunctionValue(parameters);
269: }
270:
271: public S getFunctionValue(Parameters parameters) {
272: return f.getFunctionValue(parameters);
273: }
274: };
275: } else {
276: return null;
277: }
278: }
279: }
280:
281: /**
282: * This represents the function on one specific Node. This is instantiated when new Istance
283: * if called on a NodeFunction.
284: */
285: private class NodeInstanceFunction extends WrappedFunction<R> {
286:
287: protected MMObjectNode node;
288:
289: public NodeInstanceFunction(MMObjectNode node) {
290: super (NodeFunction.this );
291: this .node = node;
292: }
293:
294: //javadoc inherited
295: public final R getFunctionValue(Parameters parameters) {
296: parameters.set(Parameter.CORENODE, node);
297: return NodeFunction.this .getFunctionValue(node, parameters);
298:
299: }
300:
301: public String toString() {
302: return NodeFunction.this .toString() + " for node "
303: + node.getNumber();
304: }
305: }
306:
307: }
|