/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jbpm.wire.binding;
import java.util.ArrayList;
import java.util.List;
import org.jbpm.wire.Descriptor;
import org.jbpm.wire.descriptor.ArgDescriptor;
import org.jbpm.wire.descriptor.ObjectDescriptor;
import org.jbpm.wire.operation.Operation;
import org.jbpm.wire.xml.WireParser;
import org.jbpm.xml.Binding;
import org.jbpm.xml.Parse;
import org.jbpm.xml.Parser;
import org.jbpm.xml.XmlUtil;
import org.w3c.dom.Element;
/**
* <p>This {@link Binding} specifies a {@link ObjectDescriptor} (to create an object).</p>
* <p> Objects can be instantiated from a constructor or from a method invocation.</p>
*
* <p>An object descriptor is defined by a <b><code>{@literal <object>}</code></b> element.</p>
*
* <p>This element can have an attribute "name", which specifies the name of the created object in the WireContext.</p>
* <p>This element can have an attribute "auto-wire", which specifies if auto-wire is enabled (see <a href='#autowire'>Auto Wiring</a>)</p>
*
* <p>The method to create the object is specified with one of these methods (see <a href='#create'>creating objects</a>):
* <ul>
* <li>attribute "class" in the xml element</li>
* <li>attribute "factory" in the xml element</li>
* <li>element "factory" as a child of the xml element</li>
* </ul>
* Only one of these methods can be used.
* </p>
*
* <p>The method attribute specifies a method to call on the object.
* If the method is called on a previously created object,
* the access to this object is specified by the "factory" element or attribute.
* If the method is static, the class is specified by the "class" attribute.
* The arguments are taken from the {@literal <arg>} children elements.</p>
*
* <h3 id='create'>Creating objects</h3>
* <h4>Creating object from a constructor</h4>
*
* <p>This method uses the "class" attribute.</p>
*
* <p>The constructor is specified by the {@literal <constructor>} element.
* It contains a list of {@literal <arg>} elements, which are the arguments to give to the constructor.
* </p>
*
* <p><i>the method attribute must not be used if the {@literal <constructor>} element is specified.</i></p>
*
* <h5>Example</h5>
* Consider the following class:
* <pre> public class Foo {
* String bar;
* public Foo(String text) {
* bar = text;
* }
* }</pre>
*
* The following Xml declaration will create an object 'o' of class 'Foo'.
* The object will be constructed by calling <code>new Foo("hello")</code>.
*
* <pre>{@literal <objects>
* <object name='o' class='Foo'>
* <constructor>
* <arg>
* <string value='hello' />
* </arg>
* </constructor>
* </object>
* </objects>}</pre>
*
* <h4>Creating an object from a method invocation</h4>
*
* <p>The name of the method to call is specified by the method attribute.</p>
* <ul>
* <li>If the method is <i>static</i>, the related class is declared by the "class" attribute.</li>
* <li>If the method is an object method, to object to which the method will be applied is defined by the 'factory' element or attribute.
* The object will be fetched from the context ('factory' attribute) or created from a descriptor (contained in the 'factory' element).</li>
* </ul>
* <p>The object constructed by the resulting {@link ObjectDescriptor} is the object returned by the call to the specified method.</p>
*
*
* <h3>Initializing Objects</h3>
* <h4 id='autowire'>Auto Wiring</h4>
* <p>If the auto wiring is enabled for the object (the attribute "auto-wire" is set to "true", "enabled" or "on"),
* the WireContext will try to look for objects with the same name as the fields in the class.
* If it finds an object with that name, and if it is assignable to the field's type, it is automatically injected,
* without the need for explicit <field> tag that specifies the injection in the wiring xml.</p>
* <p>If the auto wiring is enabled and the WireContext finds an object with the name of a field, but not assignable to this field,
* a warning message is generated.</p>
* <p>Auto-wiring is disabled by default (if no auto-wire attribute is found, a default value "auto-wire=disabled" is used).</p>
*
* <h4>Operations</h4>
* <p>Field injection or property injection are done after the auto-wiring. For more information, see {@link Operation}.</p>
*
* <p>If a field was injected by auto-wiring, its value can be overridden by specifying a <field> or <property> operation.</p>
*
* <p>Known Xml tags to specify an {@link Operation}:
* <ul>
* <li><b><code>{@literal <property .../>}</code></b>: see {@link PropertyBinding}</li>
* <li><b><code>{@literal <field .../>}</code></b>: see {@link FieldBinding}</li>
* <li><b><code>{@literal <invoke .../>}</code></b>: see {@link InvokeBinding}</li>
* <li><b><code>{@literal <subscribe .../>}</code></b>: see {@link SubscribeBinding}</li>
* <li><b><code>{@literal <enlist .../>}</code></b>: see {@link EnlistBinding}</li>
* </ul>
* </p>
* @author Tom Baeyens
* @author Guillaume Porcher (documentation)
*
* @see WireParser
*/
public class ObjectBinding implements Binding {
public Object parse(Element element, Parse parse, Parser parser) {
ObjectDescriptor descriptor = new ObjectDescriptor();
WireParser wireParser = (WireParser) parser;
String className = XmlUtil.attribute(element, "class");
String factoryObjectName = XmlUtil.attribute(element, "factory");
Element factoryElement = XmlUtil.element(element, "factory");
if (className!=null) {
descriptor.setClassName(className);
if (factoryObjectName!=null) {
parse.addProblem("attribute factory is specified together with attribute class in element object: "+XmlUtil.toString(element));
}
if (factoryElement!=null) {
parse.addProblem("element factory is specified together with attribute class in element object: "+XmlUtil.toString(element));
}
Element constructorElement = XmlUtil.element(element, "constructor");
if (constructorElement!=null) {
List<Element> argElements = XmlUtil.elements(constructorElement, "arg");
List<ArgDescriptor> argDescriptors = wireParser.parseArgs(argElements, parse);
descriptor.setArgDescriptors(argDescriptors);
if (element.hasAttribute("method")) {
parse.addProblem("attributes class and method indicate static method and also a constructor element is specified for element object: "+XmlUtil.toString(element));
}
}
} else if (factoryObjectName!=null) {
descriptor.setFactoryObjectName(factoryObjectName);
if (factoryElement!=null) {
parse.addProblem("element factory is specified together with attribute factory in element object: "+XmlUtil.toString(element));
}
} else if (factoryElement!=null) {
Element factoryDescriptorElement = XmlUtil.element(factoryElement);
Descriptor factoryDescriptor = (Descriptor) parser.parseElement(factoryDescriptorElement, parse, WireParser.CATEGORY_DESCRIPTOR);
descriptor.setFactoryDescriptor(factoryDescriptor);
} else {
parse.addProblem("element object must have one of {attribute class, attribute factory or element factory}: "+XmlUtil.toString(element));
}
// method
if (element.hasAttribute("method")) {
descriptor.setMethodName(element.getAttribute("method"));
List<Element> argElements = XmlUtil.elements(element, "arg");
List<ArgDescriptor> argDescriptors = wireParser.parseArgs(argElements, parse);
descriptor.setArgDescriptors(argDescriptors);
} else if (className == null) {
parse.addProblem("element object with a element factory or a attribute factory must have a attribute method: "+XmlUtil.toString(element));
}
if( (className == null) && (XmlUtil.element(element, "constructor") != null)){
parse.addProblem("element object with a element factory or a attribute factory can't have a constructor element: "+XmlUtil.toString(element));
}
// read the operations elements
List<Operation> operations = null;
List<Element> elements = XmlUtil.elements(element);
if (elements!=null) {
for (Element childElement: elements) {
if(!childElement.getTagName().equals("constructor")
&& !childElement.getTagName().equals("factory")
&& !childElement.getTagName().equals("arg")){
Operation operation = (Operation) parser.parseElement(childElement, parse, WireParser.CATEGORY_OPERATION);
if (operation!=null) {
if (operations==null) {
operations = new ArrayList<Operation>();
}
operations.add(operation);
}else{
parse.addProblem("element object can only have factory, arg, constructor elements or " +
"an operation element ("+ parser.getBindings().getTagNames(WireParser.CATEGORY_OPERATION) +")." +
" Invalid element '"+childElement.getTagName()+"' in: "+XmlUtil.toString(element));
}
}
}
}
descriptor.setOperations(operations);
// autowiring
String autoWireText = XmlUtil.attribute(element, "auto-wire");
Boolean isAutoWireEnabled = XmlUtil.booleanEquals(autoWireText, Boolean.FALSE);
if (isAutoWireEnabled==null) {
parse.addProblem("invalid auto-wire value: '"+autoWireText+"' in "+XmlUtil.toString(element));
} else if (isAutoWireEnabled) {
descriptor.setAutoWireEnabled(true);
}
return descriptor;
}
}