- Better name for XWiki.ComponentInterfaceClass ?
(It's the class 
 representing an extra interface the component implementation
 implements)=
 Note: module right now relies on XWIKI-5194 (bridge event for XWiki 
 initialized)
being applied to xwiki-core/xwiki-bridge
 
 [...]
 Added: 
contrib/sandbox/xwiki-wiki-components/src/main/java/com/xpn/xwiki/internal/DefaultWikiComponentBuilder.java
===================================================================
 --- 
contrib/sandbox/xwiki-wiki-components/src/main/java/com/xpn/xwiki/internal/DefaultWikiComponentBuilder.java
                         (rev 0)
  +++ 
contrib/sandbox/xwiki-wiki-components/src/main/java/com/xpn/xwiki/internal/DefaultWikiComponentBuilder.java
 2010-05-15 15:08:25 UTC (rev 28878)
  @@ -0,0 +1,167 @@ 
 [...]
  +/**
 + * Default implementation of a wiki component builder, that is 
 using the legacy
XWiki core module.
  + *
 + * @since 2.4-M2
 + * @version $Id$
 + */
 +@Component
 +public class DefaultWikiComponentBuilder implements 
 WikiComponentBuilder
  +{
 +
 +    /**
 +     * The name of the document that holds the XClass definition of 
 an
implementation of an interface by a component.
  +     */
 +    private static final String XWIKI_COMPONENT_INTERFACE_CLASS = 
"XWiki.ComponentInterfaceClass";
  +
 +    /**
 +     * The name of the document that holds the XClass definition of 
 a method of a
component.
  +     */
 +    private static final String XWIKI_COMPONENT_METHOD_CLASS = 
"XWiki.ComponentMethodClass";
  +
 +    /**
 +     * The property name of the name of a component method.
 +     */
 +    private static final String COMPONENT_METHOD_NAME_FIELD = 
 "name";
  +
 +    /**
 +     * The property name of the name of an implemented interface. 
 (Checkstyle
fix).
  +     */
 +    private static final String COMPONENT_INTERFACE_NAME_FIELD = 
COMPONENT_METHOD_NAME_FIELD;
  +
 +    /**
 +     * Execution, needed to access the XWiki context map.
 +     */
 +    @Requirement
 +    private Execution execution;
 +
 +    /**
 +     * {@inheritDoc}
 +     */
 +    public WikiComponent build(DocumentReference reference) throws 
InvalidComponentDefinitionException,
  +        WikiComponentException
 +    {
 +        try {
 +            XWikiDocument componentDocument = 
getXWikiContext().getWiki().getDocument(reference, getXWikiContext());
  +            BaseObject componentObject = 
componentDocument.getObject("XWiki.ComponentClass");
  +
 +            if (componentObject == null) {
 +                throw new InvalidComponentDefinitionException("No 
 component
object could be found");
  +            }
 +
 +            String role = componentObject.getStringValue("role");
 +
 +            if (StringUtils.isBlank(role)) {
 +                throw new InvalidComponentDefinitionException("No 
 role were
precised in the component");
  +            }
 +
 +            Class< ? > roleAsClass;
 +            try {
 +                roleAsClass = Class.forName(role);
 +            } catch (ClassNotFoundException e) {
 +                throw new InvalidComponentDefinitionException("The 
 role
class could not be found", e);
  +            }
 +
 +            String roleHint = 
StringUtils.defaultIfEmpty(componentObject.getStringValue("roleHint"),
 "default");
  +
 +            DefaultWikiComponent component = new 
 DefaultWikiComponent(reference,
roleAsClass, roleHint);
  + 
 component.setHandledMethods(this.getHandledMethods(componentDocument));
  + 
 component.setImplementedInterfaces(this.getDeclaredInterfaces(componentDocument));
  + 
 > +            return component;
  + 
 > +        } catch (XWikiException e)
{
 > +            throw new WikiComponentException("Failed to build wiki
 component for document " + reference.toString());
 > +        }
 > +    }
  + 
 > +    /**
 > +     * @param componentDocument the document holding the component
 description
  +     * @return the map of component handled
methods/method body
 +     */
 +    private Map<String, String> getHandledMethods(XWikiDocument 
componentDocument)
  +    {
 +        Map<String, String> handledMethods = new HashMap<String, 
String>();
  +        if 
(componentDocument.getObjectNumbers(XWIKI_COMPONENT_METHOD_CLASS) > 0)
 {
  +            for (BaseObject iface : 
componentDocument.getObjects(XWIKI_COMPONENT_METHOD_CLASS)) {
  +                if 
(!StringUtils.isBlank(iface.getStringValue(COMPONENT_METHOD_NAME_FIELD)))
 {
  + 
 handledMethods.put(iface.getStringValue(COMPONENT_METHOD_NAME_FIELD),
 iface.getStringValue("code"));
 > +                }
 > +            }
 > +        }
 > +        return handledMethods;
  + 
 > +    }
  + 
 > +    /**
 > +     * @param componentDocument the document holding the component
 description
  +     * @return the array of interfaces declared
(and actually 
 existing) by the document
  +     */
 +    private Class< ? >[] getDeclaredInterfaces(XWikiDocument 
componentDocument)
 > +    {
 > +        List<Class< ? >> interfaces = new ArrayList<Class< ?
>>();
  +        if 
(componentDocument.getObjectNumbers(XWIKI_COMPONENT_INTERFACE_CLASS) >
 0) {
  +            for (BaseObject iface : 
componentDocument.getObjects(XWIKI_COMPONENT_INTERFACE_CLASS)) {
  +                if 
(!StringUtils.isBlank(iface.getStringValue(COMPONENT_INTERFACE_NAME_FIELD)))
 {
  +                    try {
 +                        Class< ? > implemented = 
Class.forName(iface.getStringValue(COMPONENT_INTERFACE_NAME_FIELD));
 > +                        interfaces.add(implemented);
 > +                    } catch (ClassNotFoundException e) {
 > +                        // Silent
 > +                    }
 > +                }
 > +            }
 > +        }
 > +        return interfaces.toArray(new Class< ? >[] {});
 > +    }
  + 
 > +    /**
 > +     * @return a XWikiContext, retrieved from our execution
 > +     */
 > +    private XWikiContext getXWikiContext()
 > +    {
 > +        return (XWikiContext)
 execution.getContext().getProperty("xwikicontext");
 > +    }
  + 
 > +}
 Added: 
contrib/sandbox/xwiki-wiki-components/src/main/java/com/xpn/xwiki/internal/WikiComponentInitializer.java
===================================================================
 --- 
contrib/sandbox/xwiki-wiki-components/src/main/java/com/xpn/xwiki/internal/WikiComponentInitializer.java
                            (rev 0)
  +++ 
contrib/sandbox/xwiki-wiki-components/src/main/java/com/xpn/xwiki/internal/WikiComponentInitializer.java
    2010-05-15 15:08:25 UTC (rev 28878)
  @@ -0,0 +1,339 @@ 
 [...]
  +/**
 + * Initializes the Wiki Component feature. First ensure all needed 
 XClasses are
up-to-date, then registers existing
  + * components.
 + *
 + * @since 2.4-M2
 + * @version $Id$
 + */
 +@Component("wikiComponentInitializer")
 +public class WikiComponentInitializer extends AbstractLogEnabled 
 implements
EventListener
  +{
 +    /**
 +     * The XClass defining a component implementation.
 +     */
 +    private static final String WIKI_COMPONENT_CLASS = 
"XWiki.ComponentClass";
  + 
 > +    /**
 > +     * The XClass defining a component requirement.
 > +     */
 > +    private static final String WIKI_COMPONENT_REQUIREMENT_CLASS =
 "XWiki.ComponentRequirementClass";
  + 
 > +    /**
 > +     * The XClass defining a component method.
 > +     */
 > +    private static final String WIKI_COMPONENT_METHOD_CLASS =
 "XWiki.ComponentMethodClass";
  + 
 > +    /**
 > +     * The XClass defining a component interface implementation.
 > +     */
 > +    private static final String WIKI_COMPONENT_INTERFACE_CLASS =
 "XWiki.ComponentInterfaceClass";
  + 
 > +    /**
 > +     * The name property of the {@link
 WIKI_COMPONENT_INTERFACE_CLASS} XClass.
 > +     */
 > +    private static final String INTERFACE_NAME_FIELD = "name";
  + 
 > +    /**
 > +     * The name property of the {@link WIKI_COMPONENT_METHOD_CLASS}
 XClass. (Fix checkstyle).
  +     */
 +    private static final String METHOD_NAME_FIELD = 
 INTERFACE_NAME_FIELD;
  + 
 > +    /**
 > +     * The role property of both {@link WIKI_COMPONENT_CLASS} and
 {@link WIKI_COMPONENT_REQUIREMENT_CLASS}.
  +     */
 +    private static final String COMPONENT_ROLE_HINT_FIELD = 
 "roleHint";
  + 
 > +    /**
 > +     * The role hint property of both {@link WIKI_COMPONENT_CLASS}
 and {@link WIKI_COMPONENT_REQUIREMENT_CLASS}.
 > +     */
 > +    private static final String COMPONENT_ROLE_FIELD = "role";
  + 
 > +    /**
 > +     * Our execution. Needed to access the XWiki context.
 > +     */
 > +    @Requirement
 > +    private Execution execution;
  + 
 > +    /**
 > +     * The wiki component manager that knows how to register
 component definition against the underlying CM.
 > +     */
 > +    @Requirement
 > +    private WikiComponentManager wikiComponentManager;
  + 
 > +    /**
 > +     * Builder that creates component description from document
 references.
 > +     */
 > +    @Requirement
 > +    private WikiComponentBuilder wikiComponentBuilder;
  + 
 > +    /**
 > +     * {@inheritDoc}
 > +     */
 > +    public List<Event> getEvents()
 > +    {
 > +        return Arrays.<Event> asList(new
 XWikiInitializedBridgeEvent());
 > +    }
  + 
 > +    /**
 > +     * {@inheritDoc}
 > +     */
 > +    public String getName()
 > +    {
 > +        return "wikiComponentInitializer";
 > +    }
  + 
 > +    /**
 > +     * {@inheritDoc}
 > +     */
 > +    public void onEvent(Event arg0, Object arg1, Object arg2)
 > +    {
 > +        // First step, verify that all XClasses exists and are
 up-to-date (act if not).
 > +        this.installOrUpdateComponentXClasses();
 > +        // Second step, lookup and register existing components.
 > +        this.registerExistingWikiComponents();
 > +    }
  + 
 > +    /**
 > +     * Registers existing components. Query them against the store,
 and if they are built properly (valid definition)
  +     * register them against the CM.
 +     */
 +    private void registerExistingWikiComponents()
 +    {
 +        String query =
 +            ", BaseObject as obj, StringProperty as role where 
obj.className='XWiki.ComponentClass'"
  +                + " and
obj.name=doc.fullName and role.id.id=obj.id 
 and role.id.name='role' and
role.value <>''";
  +        try {
 +            for (DocumentReference ref : 
getXWikiContext().getWiki().getStore().searchDocumentReferences(query,
  +                getXWikiContext())) {
 +                try {
 +                    WikiComponent component = 
this.wikiComponentBuilder.build(ref);
  + 
+ 
  this.wikiComponentManager.registerWikiComponent(component);
  +                } catch
(InvalidComponentDefinitionException e) {
 +                    // Fail quietly and only log at the debug 
 level.
  +                  
 getLogger().debug("Invalid wiki component 
 definition for reference " +
ref.toString(), e);
  +                } catch (WikiComponentException
e) {
 +                    // Fail quietly and only log at the debug 
 level.
  +                  
 getLogger().debug("Failed to register wiki 
 component for reference " +
ref.toString(), e);
  +                }
 +            }
 +        } catch (XWikiException e) {
 +            getLogger().error("Failed to register existing wiki 
components", e);
 > +        }
  + 
 > +    }
  + 
 > +    /**
 > +     * Verify that all XClasses exists and are up-to-date (act if
 not).
  +     */
 +    private void installOrUpdateComponentXClasses()
 +    {
 +        try {
 +            this.installOrUpdateComponentXClass();
 +            this.installOrUpdateComponentRequirementXClass();
 +            this.installOrUpdateComponentMethodXClass();
 +            this.installOrUpdateComponentInterfaceXClass();
 +        } catch (XWikiException e) {
 +            getLogger().error("Failed to install or update wiki 
 component
XClasses", e);
 > +        }
 > +    }
  + 
 > +    /**
 > +     * Verify that the {@link #WIKI_COMPONENT_INTERFACE_CLASS}
 exists and is up-to-date (act if not).
  +     *
 +     * @throws XWikiException on failure
 +     */
 +    private void installOrUpdateComponentInterfaceXClass() throws 
 XWikiException
  +    {
 +        XWikiContext xcontext = getXWikiContext();
 +        XWikiDocument doc = 
xcontext.getWiki().getDocument(WIKI_COMPONENT_INTERFACE_CLASS,
 xcontext);
  + 
 > +        BaseClass bclass =
doc.getXClass();
 > +        bclass.setName(WIKI_COMPONENT_INTERFACE_CLASS);
  + 
 > +        boolean needsUpdate =
false;
  + 
 > +        needsUpdate |=
this.initializeXClassDocumentMetadata(doc,
 "Wiki Component Implements Interface XWiki Class");
  +        needsUpdate |=
bclass.addTextField(INTERFACE_NAME_FIELD, 
 "Interface Qualified Name",
30);
  + 
 > +        if (needsUpdate) {
 > +            this.update(doc);
 > +        }
 > +    }
  + 
 > +    /**
 > +     * Verify that the {@link #WIKI_COMPONENT_CLASS} exists and is
 up-to-date (act if not).
  +     *
 +     * @throws XWikiException on failure
 +     */
 +    private void installOrUpdateComponentXClass() throws 
 XWikiException
  +    {
 +        XWikiContext xcontext = getXWikiContext();
 +        XWikiDocument doc = 
 xcontext.getWiki().getDocument(WIKI_COMPONENT_CLASS,
xcontext);
  + 
 > +        BaseClass bclass =
doc.getXClass();
 > +        bclass.setName(WIKI_COMPONENT_CLASS);
  + 
 > +        boolean needsUpdate =
false;
  + 
 > +        needsUpdate |=
this.initializeXClassDocumentMetadata(doc,
 "Wiki Component XWiki Class");
  +        needsUpdate |=
bclass.addTextField(COMPONENT_ROLE_FIELD, 
 "Component role", 30);
  +        needsUpdate |= 
bclass.addTextField(COMPONENT_ROLE_HINT_FIELD, "Component role hint",
 30);
  + 
 > +        if (needsUpdate) {
 > +            this.update(doc);
 > +        }
 > +    }
  + 
 > +    /**
 > +     * Verify that the {@link #WIKI_COMPONENT_REQUIREMENT_CLASS}
 exists and is up-to-date (act if not).
  +     *
 +     * @throws XWikiException on failure
 +     */
 +    private void installOrUpdateComponentRequirementXClass() throws 
XWikiException
  +    {
 +        XWikiContext xcontext = getXWikiContext();
 +        XWikiDocument doc = 
xcontext.getWiki().getDocument(WIKI_COMPONENT_REQUIREMENT_CLASS,
 xcontext);
  + 
 > +        BaseClass bclass =
doc.getXClass();
 > +        bclass.setName(WIKI_COMPONENT_REQUIREMENT_CLASS);
  + 
 > +        boolean needsUpdate =
false;
  + 
 > +        needsUpdate |=
this.initializeXClassDocumentMetadata(doc,
 "Wiki Component Requirement XWiki Class");
  +        needsUpdate |=
bclass.addTextField(COMPONENT_ROLE_FIELD, 
 "Requirement role", 30);
  +        needsUpdate |= 
bclass.addTextField(COMPONENT_ROLE_HINT_FIELD, "Requirement role
 hint", 30);
  +        needsUpdate |=
bclass.addTextField("bindingName", "Binding 
 name", 30);
  +        needsUpdate |=
bclass.addStaticListField("type", 
 "Requirement type",
"single=Single|list=List|map=Map");
  + 
 > +        if (needsUpdate) {
 > +            this.update(doc);
 > +        }
 > +    }
  + 
 > +    /**
 > +     * Verify that the {@link #WIKI_COMPONENT_METHOD_CLASS} exists
 and is up-to-date (act if not).
  +     *
 +     * @throws XWikiException on failure
 +     */
 +    private void installOrUpdateComponentMethodXClass() throws 
 XWikiException
  +    {
 +        XWikiContext xcontext = getXWikiContext();
 +        XWikiDocument doc = 
xcontext.getWiki().getDocument(WIKI_COMPONENT_METHOD_CLASS, xcontext);
  + 
 > +        BaseClass bclass =
doc.getXClass();
 > +        bclass.setName(WIKI_COMPONENT_METHOD_CLASS);
  + 
 > +        boolean needsUpdate =
false;
  + 
 > +        needsUpdate |=
this.initializeXClassDocumentMetadata(doc,
 "Wiki Component Method XWiki Class");
  +        needsUpdate |=
bclass.addTextField(METHOD_NAME_FIELD, 
 "Method name", 30);
  +        needsUpdate |=
bclass.addTextAreaField("code", "Method body 
 code", 40, 20);
  + 
 > +        if (needsUpdate) {
 > +            this.update(doc);
 > +        }
 > +    }
  + 
 > +    /**
 > +     * Utility method for updating a wiki macro class definition
 document.
  +     *
 +     * @param doc xwiki document containing the wiki macro class.
 +     * @throws XWikiException if an error occurs while saving the 
 document.
 > +     */
 > +    private void update(XWikiDocument doc) throws XWikiException
 > +    {
 > +        XWikiContext xcontext = getXWikiContext();
 > +        xcontext.getWiki().saveDocument(doc, xcontext);
 > +    }
  + 
 > +    /**
 > +     * Helper method to prepare a document that will hold an XClass
 definition, setting its initial metadata, if needed
  +     * (author, title, parent, content, etc.).
 +     *
 +     * @param doc the document to prepare
 +     * @param title the title to set
 +     * @return true if the doc has been modified and needs saving, 
 false
otherwise
  +     */
 +    private boolean initializeXClassDocumentMetadata(XWikiDocument 
 doc, String
title)
 > +    {
 > +        boolean needsUpdate = false;
  + 
 > +        if
(StringUtils.isBlank(doc.getCreator())) {
 > +            needsUpdate = true;
 > +            doc.setCreator(XWikiRightService.SUPERADMIN_USER);
 > +        }
 > +        if (StringUtils.isBlank(doc.getAuthor())) {
 > +            needsUpdate = true;
 > +            doc.setAuthor(doc.getCreator());
 > +        }
 > +        if (StringUtils.isBlank(doc.getParent())) {
 > +            needsUpdate = true;
 > +            doc.setParent("XWiki.XWikiClasses");
 > +        }
 > +        if (StringUtils.isBlank(doc.getTitle())) {
 > +            needsUpdate = true;
 > +            doc.setTitle(title);
 > +        }
 > +        if (StringUtils.isBlank(doc.getContent()) ||
 !XWikiDocument.XWIKI20_SYNTAXID.equals(doc.getSyntaxId())) {
  +            needsUpdate = true;
 +            doc.setContent("{{include document=\"XWiki.ClassSheet\"
 /}}");
 > +            doc.setSyntaxId(XWikiDocument.XWIKI20_SYNTAXID);
 > +        }
 > +        return needsUpdate;
 > +    }
  + 
 > +    /**
 > +     * @return the XWikiContext extracted from the execution.
 > +     */
 > +    private XWikiContext getXWikiContext()
 > +    {
 > +        return (XWikiContext)
 this.execution.getContext().getProperty("xwikicontext");
 > +    }
  + 
 > +}
 Added: 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/InvalidComponentDefinitionException.java
===================================================================
 --- 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/InvalidComponentDefinitionException.java
                               (rev 0)
  +++ 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/InvalidComponentDefinitionException.java
       2010-05-15 15:08:25 UTC (rev 28878)
  @@ -0,0 +1,59 @@ 
 [...]
  +/**
 + * Exception thrown by component builders when a document holds an 
 invalid
compoentn definition.
 > + * (For example if no role has been specified).
 > + *
 > + *  @since 2.4-M2
 > + *  @version $Id$
 > + */
 > +public class InvalidComponentDefinitionException extends Exception
 > +{
 > +    /**
 > +     * Constructor of this exception.
 > +     */
 > +    public InvalidComponentDefinitionException()
 > +    {
 > +        super();
 > +    }
  + 
 > +    /**
 > +     * Constructor of this exception.
 > +     *
 > +     * @param message a message associated with the exception, that
 explains why the definition is invalid
 > +     */
 > +    public InvalidComponentDefinitionException(String message)
 > +    {
 > +        super(message);
 > +    }
  + 
 > +    /**
 > +     * Constructor of this exception.
 > +     *
 > +     * @param message a message associated with the exception, that
 explains why the definition is invalid
  +     * @param t the root cause
 +     */
 +    public InvalidComponentDefinitionException(String message, 
 Throwable t)
 > +    {
 > +        super(message);
 > +    }
 > +}
 Added: 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/MethodOutputHandler.java
===================================================================
 --- 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/MethodOutputHandler.java
                               (rev 0)
  +++ 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/MethodOutputHandler.java
       2010-05-15 15:08:25 UTC (rev 28878)
  @@ -0,0 +1,44 @@ 
 [...]
  +/**
 + * Utility for wiki methods to return a value. An implementation of 
 this
interface is binded in the context of a
  + * wiki method execution, so that such method
scripts can return a 
 value using {@link #returnValue(Object)}.
 > + *
 > + * @since 2.4-M2
 > + * @version $Id$
 > + */
 > +public interface MethodOutputHandler
 > +{
  + 
 > +    /**
 > +     * Stores a value in the method invocation context for further
 return.
  +     * Note that if this method is called
multiple times during the 
 invocation, the last one wins.
 > +     *
 > +     * @param value the value to return
 > +     */
 > +    void returnValue(Object value);
  + 
 > +    /**
 > +     * @return the current stored return value (null if not set
 yet).
 > +     */
 > +    Object getReturnValue();
 > +}
 Added: 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/WikiComponent.java
===================================================================
 --- 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/WikiComponent.java
                             (rev 0)
  +++ 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/WikiComponent.java
     2010-05-15 15:08:25 UTC (rev 28878)
  @@ -0,0 +1,58 @@ 
 [...]
  +/**
 + * Represents the definition of a wiki component implementation.
 + *
 + * @since 2.4-M2
 + * @version $Id$
 + */
 +public interface WikiComponent
 +{
 +    /**
 +     * @return the reference to the document holding this wiki 
 component
definition.
 > +     */
 > +    DocumentReference getDocumentReference();
  + 
 > +    /**
 > +     * @return the role implemented by this component
 implementation.
 > +     */
 > +    Class< ? > getRole();
  + 
 > +    /**
 > +     * @return the hint of the role implemented by this component
 implementation.
 > +     */
 > +    String getRoleHint();
  + 
 > +    /**
 > +     * @return the extra list of interfaces this component
 implementation implements.
 > +     */
 > +    Class< ? >[] getImplementedInterfaces();
  + 
 > +    /**
 > +     * @return the map of method name/wiki code this component
 implementation handles.
 > +     */
 > +    Map<String, String> getHandledMethods();
 > +}
 Added: 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/WikiComponentBuilder.java
===================================================================
 --- 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/WikiComponentBuilder.java
                              (rev 0)
  +++ 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/WikiComponentBuilder.java
      2010-05-15 15:08:25 UTC (rev 28878)
  @@ -0,0 +1,46 @@ 
 [...]
  +/**
 + * Constructs a {@link WikiComponent} out of the data contained in 
 the document
pointed by a {@link DocumentReference}.
 > + *
 > + * @since 2.4-M2
 > + * @version $Id$
 > + */
 > +@ComponentRole
 > +public interface WikiComponentBuilder
 > +{
  + 
 > +    /**
 > +     * Builds a wiki component representation extracting the data
 stored as objects of document.
  +     *
 +     * @param reference the reference to the document that holds 
 component
definition objects
  +     * @return the constructed component
definition
 +     * @throws InvalidComponentDefinitionException when the data in 
 the document
is not a valid component definition
  +     * @throws WikiComponentException the
builder failed to create 
 the component out of the document (for example due to
  +     *             a failure by tge underlying
store, etc.)
 +     */
 +    WikiComponent build(DocumentReference reference) throws 
InvalidComponentDefinitionException, WikiComponentException;
  + 
 > +}
 Added: 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/WikiComponentException.java
===================================================================
 --- 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/WikiComponentException.java
                            (rev 0)
  +++ 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/WikiComponentException.java
    2010-05-15 15:08:25 UTC (rev 28878)
  @@ -0,0 +1,51 @@ 
 [...]
  +/**
 + * A generic exception thrown by this module, usually a wrapper 
 around lower
level exceptions.
 > + *
 > + * @since 2.4-M2
 > + * @version $Id$
 > + */
 > +public class WikiComponentException extends Exception
 > +{
 > +    /**
 > +     * Constructor of this exception.
 > +     *
 > +     * @param message the message associated with the exception
 > +     */
 > +    public WikiComponentException(String message)
 > +    {
 > +        super(message);
 > +    }
  + 
 > +    /**
 > +     * Constructor of this exception.
 > +     *
 > +     * @param message the message associated with the exception
 > +     * @param t the root cause
 > +     */
 > +    public WikiComponentException(String message, Throwable t)
 > +    {
 > +        super(message, t);
 > +    }
  + 
 > +}
 Added: 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/WikiComponentInvocationHandler.java
===================================================================
 --- 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/WikiComponentInvocationHandler.java
                            (rev 0)
  +++ 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/WikiComponentInvocationHandler.java
    2010-05-15 15:08:25 UTC (rev 28878)
  @@ -0,0 +1,245 @@ 
 [...]
  +/**
 + * Method invocation handler for wiki component proxy instances. 
 Has a reference
on a map of name/body wiki code of
  + * supported methods.
 + *
 + * @since 2.4-M2
 + * @version $Id$
 + */
 +public class WikiComponentInvocationHandler implements 
 InvocationHandler
  +{
 +    /**
 +     * The key under which the output is kept in the method 
 invocation context.
  +     */
 +    private static final String METHOD_CONTEXT_OUTPUT_KEY = 
 "output";
  + 
 > +    /**
 > +     * The key under which the context document is kept in the
 XWiki context.
 > +     */
 > +    private static final String XWIKI_CONTEXT_DOC_KEY = "doc";
  + 
 > +    /**
 > +     * Pre-loaded hasCode Method.
 > +     *
 > +     * @see {@link Object#hashCode()}
 > +     */
 > +    private static Method hashCodeMethod;
  + 
 > +    /**
 > +     * Pre-loaded equals method.
 > +     *
 > +     * @see {@link Object#equals(Object)}
 > +     */
 > +    private static Method equalsMethod;
  + 
 > +    /**
 > +     * Pre-loaded toString method.
 > +     *
 > +     * @see {@link Object#toString()}
 > +     */
 > +    private static Method toStringMethod;
  + 
 > +    static {
 > +        try {
 > +            hashCodeMethod = Object.class.getMethod("hashCode",
 null);
  +            equalsMethod =
Object.class.getMethod("equals", new 
 Class[] {Object.class});
  +            toStringMethod =
Object.class.getMethod("toString", 
 null);
 > +        } catch (NoSuchMethodException e) {
 > +            throw new NoSuchMethodError(e.getMessage());
 > +        }
 > +    }
  + 
 > +    /**
 > +     * Map hosting handled methods. Keys are method names and
 values are wiki code to "execute".
 > +     */
 > +    private Map<String, String> handledMethods;
  + 
 > +    /**
 > +     * Our component manager.
 > +     */
 > +    private ComponentManager componentManager;
  + 
 > +    /**
 > +     * The reference to the document.
 > +     */
 > +    private DocumentReference componentReference;
  + 
 > +    /**
 > +     * Constructor of this invocation handler.
 > +     *
 > +     * @param componentReference reference to the document holding
 the component definition
  +     * @param methods the map of methods handled
by the component 
 instance
  +     * @param componentManager the component
manager
 +     */
 +    public WikiComponentInvocationHandler(DocumentReference 
 componentReference,
Map<String, String> methods,
 > +        ComponentManager componentManager)
 > +    {
 > +        this.componentReference = componentReference;
 > +        this.handledMethods = methods;
 > +        this.componentManager = componentManager;
 > +    }
  + 
 > +    /**
 > +     * {@inheritDoc}
 > +     */
 > +    public Object invoke(Object proxy, Method method, Object[]
 args) throws Throwable
 > +    {
 > +        if (!this.handledMethods.containsKey(method.getName())) {
 > +            if (method.getDeclaringClass() == Object.class) {
 > +                return this.proxyObjectMethod(proxy, method, args);
 > +            } else {
 > +                throw new NoSuchMethodException();
 > +            }
 > +        } else {
 > +            return this.executeWikiContent(method);
 > +        }
  + 
 > +    }
  + 
 > +    /**
 > +     * "Executes" the wiki content associated to the passed method.
 > +     *
 > +     * @param method the method to execute
 > +     * @return the result of the execution
 > +     * @throws Exception when an error occurs during execution
 > +     */
 > +    @SuppressWarnings("unchecked")
 > +    private Object executeWikiContent(Method method) throws
 Exception
 > +    {
 > +        XDOM xdom;
 > +        Map xwikiContext = null;
 > +        Object contextDoc = null;
  + 
 > +        Parser parser =
componentManager.lookup(Parser.class,
 Syntax.XWIKI_2_0.toIdString());
  +        xdom = parser.parse(new 
StringReader(this.handledMethods.get(method.getName())));
  + 
 > +        Execution execution =
 componentManager.lookup(Execution.class);
  +        Transformation macroTransformation =
 componentManager.lookup(Transformation.class, "macro");
  +        DocumentAccessBridge docBridge = 
componentManager.lookup(DocumentAccessBridge.class);
  + 
 > +        Map<String, Object>
methodContext = new HashMap<String,
 Object>();
  +      
 methodContext.put(METHOD_CONTEXT_OUTPUT_KEY, new 
 DefaultMethodOutputHandler());
  + 
 > +        // Place macro context
inside xwiki context
 ($context.macro).
  +        xwikiContext = (Map) 
execution.getContext().getProperty("xwikicontext");
  +        xwikiContext.put("method",
methodContext);
 +        // Save current context document.
 +        contextDoc = xwikiContext.get(XWIKI_CONTEXT_DOC_KEY);
 +        // Make sure has prog rights
 +        xwikiContext.put(XWIKI_CONTEXT_DOC_KEY, 
docBridge.getDocument(this.componentReference));
  + 
 > +        // Perform internal macro
transformations.
 > +        macroTransformation.transform(xdom, Syntax.XWIKI_2_0);
  + 
 > +        if
(methodContext.get(METHOD_CONTEXT_OUTPUT_KEY) != null
 > +            && ((MethodOutputHandler)
 methodContext.get(METHOD_CONTEXT_OUTPUT_KEY)).getReturnValue() !=
 null) {
  +            return 
method.getReturnType().cast(((MethodOutputHandler)
  + 
 methodContext.get(METHOD_CONTEXT_OUTPUT_KEY)).getReturnValue());
  +        } else if
(method.getReturnType().equals(String.class)) {
 +            // If return type is String and no specific return 
 value has been
provided during the macro
  +            // expansion, then we return the
content redered as
 +            WikiPrinter printer = new DefaultWikiPrinter();
 +            BlockRenderer renderer = 
 componentManager.lookup(BlockRenderer.class,
 Syntax.PLAIN_1_0.toIdString());
  +            renderer.render(xdom, printer);
 +            return printer.toString();
 +        } else {
 +            // surrender
 +            return null;
 +        }
 +    } 
 Don't do the same mistake than wik macros (see
 
http://jira.xwiki.org/jira/browse/XWIKI-5030), you should store the
 XDOM instead of the source to avoid reparsing it each time you execute
 a virtual method. 
 
 OK. Will take care of that.
 Jerome.
  +
 +    /**
 +     * Proxies a method of the {@link Object} class.
 +     *
 +     * @param proxy the proxy instance
 +     * @param method the method to proxy
 +     * @param args possible arguments to the method invocation
 +     * @return the result of the proxied call
 +     */
 +    private Object proxyObjectMethod(Object proxy, Method method, 
 Object[] args)
  +    {
 +        if (method.equals(hashCodeMethod)) {
 +            return proxyHashCode(proxy);
 +        } else if (method.equals(equalsMethod)) {
 +            return proxyEquals(proxy, args[0]);
 +        } else if (method.equals(toStringMethod)) {
 +            return proxyToString(proxy);
 +        } else {
 +            throw new InternalError("unexpected Object method 
 dispatched:
" + method);
  +        }
 +    }
 +
 +    /**
 +     * Default behavior for {@link Object#hashCode()} when not 
 overridden in the
wiki component definition.
  +     *
 +     * @param proxy the proxy object
 +     * @return a hash code for the proxy object, as if using 
 standard
{Object{@link #hashCode()}.
  +     */
 +    protected Integer proxyHashCode(Object proxy)
 +    {
 +        return new Integer(System.identityHashCode(proxy));
 +    }
 +
 +    /**
 +     * Default behavior for {@link Object#equals(Object)} when not 
 overridden in
the wiki component definition.
  +     *
 +     * @param proxy the proxy object
 +     * @param other the other object of the comparison
 +     * @return the result of the equality comparison between the 
 passed proxy and
other object
  +     */
 +    protected Boolean proxyEquals(Object proxy, Object other)
 +    {
 +        return (proxy == other ? Boolean.TRUE : Boolean.FALSE);
 +    }
 +
 +    /**
 +     * Default behavior for {@link Object#toString()} when not 
 overridden in the
wiki component definition.
  +     *
 +     * @param proxy the proxy object
 +     * @return the String representation of the passed proxy object
 +     */
 +    protected String proxyToString(Object proxy)
 +    {
 +        return proxy.getClass().getName() + '@' + 
Integer.toHexString(proxy.hashCode());
  +    }
 +
 +}
 Added: 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/WikiComponentManager.java
===================================================================
 --- 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/WikiComponentManager.java
                              (rev 0)
  +++ 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/WikiComponentManager.java
      2010-05-15 15:08:25 UTC (rev 28878)
  @@ -0,0 +1,46 @@ 
 [...]
  +package org.xwiki.component.wiki;
 +
 +import org.xwiki.component.annotation.ComponentRole;
 +
 +/**
 + * A WikiComponentManager is responsible for registering and 
 unregistering
components that are defined as wiki pages.
  + * Each {@link WikiComponent} managed by such
manager is associated 
 to a {@link org.xwiki.model.DocumentReference}. The
  + * referred document contains XObjects that
define the role, hint 
 and behavior (method bodies) of the component. This
  + * document may also define requirements (other
components to be 
 binded in the method bodies execution context) and
  + * possible extra interfaces (for example to
implement {@link 
 org.xwiki.component.phase.Initializable}).
  + *
 + * @since 2.4-M2
 + * @version $Id$
 + */
 +@ComponentRole
 +public interface WikiComponentManager
 +{
 +
 +    /**
 +     * Registers the passed component against the underlying 
 component
repository.
  +     *
 +     * @param component the component to register
 +     * @throws WikiComponentException when failed to register the 
 component
against the CM
  +     */
 +    void registerWikiComponent(WikiComponent component) throws 
WikiComponentException;
  +
 +}
 Added: 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/internal/DefaultMethodOutputHandler.java
===================================================================
 --- 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/internal/DefaultMethodOutputHandler.java
                               (rev 0)
  +++ 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/internal/DefaultMethodOutputHandler.java
       2010-05-15 15:08:25 UTC (rev 28878)
  @@ -0,0 +1,54 @@ 
 [...]
  +package org.xwiki.component.wiki.internal;
 +
 +import org.xwiki.component.wiki.MethodOutputHandler;
 +
 +/**
 + * Default method output handler.
 + *
 + * @since 2.4-M2
 + * @version $Id$
 + */
 +public class DefaultMethodOutputHandler implements 
 MethodOutputHandler
 > +{
 > +
 > +    /**
 > +     * The stored return value.
 > +     */
 > +    private Object returnValue;
 > +
 > +    /**
 > +     * {@inheritDoc}
 > +     */
 > +    public void returnValue(Object value)
 > +    {
 > +        this.returnValue = value;
 > +    }
 > +
 > +    /**
 > +     * {@inheritDoc}
 > +     */
 > +    public Object getReturnValue()
 > +    {
 > +        return this.returnValue;
  +    }
 +
 +}
 Added: 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/internal/DefaultWikiComponent.java
===================================================================
 --- 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/internal/DefaultWikiComponent.java
                             (rev 0)
  +++ 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/internal/DefaultWikiComponent.java
     2010-05-15 15:08:25 UTC (rev 28878)
  @@ -0,0 +1,139 @@ 
 [...]
  +package org.xwiki.component.wiki.internal;
 +
 +import java.util.HashMap;
 +import java.util.Map;
 +
 +import org.xwiki.component.wiki.WikiComponent;
 +import org.xwiki.model.reference.DocumentReference;
 +
 +/**
 + * Default implementation of a wiki component definition.
 + *
 + * @since 2.4-M2
 + * @version $Id$
 + */
 +public class DefaultWikiComponent implements WikiComponent
 +{
 +    /**
 +     * @see {@link #getDocumentReference()}
 +     */
 +    private DocumentReference documentReference;
 +
 +    /**
 +     * @see {@link #getHandledMethods()}
 +     */
 +    private Map<String, String> handledMethods = new 
 HashMap<String,
String>();
  +
 +    /**
 +     * @see {@link #getRole()}
 +     */
 +    private Class< ? > role;
 +
 +    /**
 +     * @see {@link #getRoleHint()}
 +     */
 +    private String roleHint;
 +
 +    /**
 +     * @see {@link #getImplementedInterfaces()}
 +     */
 +    private Class< ? >[] implementedInterfaces = new Class< ?
[]{};
 +
 +    /**
 +     * Constructor of this component.
 +     *
 +     * @param reference the document holding the component 
 definition
  +     * @param role the role implemented
 +     * @param roleHint the role hint for this role implementation
 +     */
 +    public DefaultWikiComponent(DocumentReference reference, Class< 
 ? >
role, String roleHint)
  +    {
 +        this.documentReference = reference;
 +        this.role = role;
 +        this.roleHint = roleHint;
 +    }
 +
 +    /**
 +     * {@inheritDoc}
 +     */
 +    public DocumentReference getDocumentReference()
 +    {
 +        return this.documentReference;
 +    }
 +
 +    /**
 +     * {@inheritDoc}
 +     */
 +    public Map<String, String> getHandledMethods()
 +    {
 +        return this.handledMethods;
 +    }
 +
 +    /**
 +     * {@inheritDoc}
 +     */
 +    public Class< ? > getRole()
 +    {
 +        return this.role;
 +    }
 +
 +    /**
 +     * {@inheritDoc}
 +     */
 +    public String getRoleHint()
 +    {
 +        return this.roleHint;
 +    }
 +
 +    /**
 +     * {@inheritDoc}
 +     */
 +    public Class< ? >[] getImplementedInterfaces()
 +    {
 +        return this.implementedInterfaces;
 +    }
 +
 +    /**
 +     * Sets the handled method.
 +     *
 +     * @see {@link #getHandledMethods()}
 +     *
 +     * @param methods the methods this component will handle
 +     */
 +    public void setHandledMethods(Map<String, String> methods)
 +    {
 +        this.handledMethods = methods;
 +    }
 +
 +    /**
 +     * Sets the implemented interfaces.
 +     *
 +     * @see {@link #getImplementedInterfaces()}
 +     *
 +     * @param interfaces the interfaces this component will 
 implement.
 > +     */
 > +    public void setImplementedInterfaces(Class< ? >[] interfaces)
 > +    {
 > +        this.implementedInterfaces = interfaces;
  +    }
 +
 +}
 Added: 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/internal/DefaultWikiComponentManager.java
===================================================================
 --- 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/internal/DefaultWikiComponentManager.java
                              (rev 0)
  +++ 
contrib/sandbox/xwiki-wiki-components/src/main/java/org/xwiki/component/wiki/internal/DefaultWikiComponentManager.java
      2010-05-15 15:08:25 UTC (rev 28878)
  @@ -0,0 +1,130 @@ 
 [...]
  +
 +/**
 + * Default implementation of {@link WikiComponentManager}. Creates 
 proxy objects
which method invocation handler keeps a
  + * reference on a set of declared method and
associated wiki 
 content to "execute".
  + *
 + * @since 2.4-M2
 + * @version $Id$
 + */
 +@Component
 +public class DefaultWikiComponentManager extends AbstractLogEnabled 
 implements
WikiComponentManager
  +{
 +    /**
 +     * Component manager against which wiki component will be 
 registered.
  +     */
 +    @Requirement
 +    private ComponentManager mainComponentManager;
 +
 +    /**
 +     * {@inheritDoc}
 +     */
 +    @SuppressWarnings("unchecked")
 +    public void registerWikiComponent(WikiComponent component) 
 throws
WikiComponentException
  +    {
 +        try {
 +            // Get the component role interface
 +            Class< ? > role = component.getRole();
 +
 +            // Create the method invocation handler of the proxy
 +            InvocationHandler handler =
 +                new 
WikiComponentInvocationHandler(component.getDocumentReference(),
 component.getHandledMethods(),
  +                    mainComponentManager);
 +
 +            // Prepare array of all interfaces the component 
 implementation
declares, that is the interface declared as
  +            // component role
 +            // plus possible extra other interfaces
 +            Class< ? >[] allImplementedInterfaces = new Class< ?
[component.getImplementedInterfaces().length + 1];
 +            System.arraycopy(component.getImplementedInterfaces(), 
 0,
allImplementedInterfaces, 0, component
  +              
 .getImplementedInterfaces().length);
 + 
  allImplementedInterfaces[component.getImplementedInterfaces().length]
 = role;
  +
 +            // Create the component instance and its descritor
 +            Object instance = 
 Proxy.newProxyInstance(role.getClassLoader(),
 allImplementedInterfaces, handler);
  +            ComponentDescriptor
componentDescriptor = 
 this.createComponentDescriptor(role,
component.getRoleHint());
  +
 +            // Since we are responsible to create the component 
 instance,
  +            // we also are responsible of its
initialization (if 
 needed)
  +            if
(this.isInitializable(allImplementedInterfaces)) {
 +                try {
 +                    ((Initializable) instance).initialize();
 +                } catch (InitializationException e) {
 +                    getLogger().error("Failed to initialize wiki 
component", e);
  +                }
 +            }
 +
 +            // Finally, register the component against the CM
 + 
  this.mainComponentManager.registerComponent(componentDescriptor,
 role.cast(instance));
  +        } catch (ComponentRepositoryException e)
{
 +            throw new WikiComponentException("Failed to register 
 wiki
component against component repository", e);
  +        }
 +    }
 +
 +    /**
 +     * Helper method to create a component descriptor from role and 
 hint.
  +     *
 +     * @param role the component role of the descriptor to create
 +     * @param roleHint the hint of the implementation for the 
 descriptor to
create
  +     * @return the constructed {@link
ComponentDescriptor}
 +     */
 +    @SuppressWarnings("unchecked")
 +    private ComponentDescriptor createComponentDescriptor(Class 
 role, String
roleHint)
  +    {
 +        DefaultComponentDescriptor cd = new 
 DefaultComponentDescriptor();
  +        cd.setRole(role);
 +        cd.setRoleHint(roleHint);
 +        return cd;
 +    }
 +
 +    /**
 +     * Helper method that checks if at least one of an array of 
 interfaces is the
{@link Initializable} class.
  +     *
 +     * @param interfaces the array of interfaces to test
 +     * @return true if at least one of the passed interfaces is the 
 is the {@link
Initializable} class.
  +     */
 +    private boolean isInitializable(Class< ? >[] interfaces)
 +    {
 +        for (Class< ? > iface : interfaces) {
 +            if (Initializable.class.equals(iface)) {
 +                return true;
 +            }
 +        }
 +        return false;
 +    }
 +}
 
 [...]
 Added: 
contrib/sandbox/xwiki-wiki-components/src/test/java/org/xwiki/component/wiki/WikiComponentManagerTest.java
===================================================================
 --- 
contrib/sandbox/xwiki-wiki-components/src/test/java/org/xwiki/component/wiki/WikiComponentManagerTest.java
                          (rev 0)
  +++ 
contrib/sandbox/xwiki-wiki-components/src/test/java/org/xwiki/component/wiki/WikiComponentManagerTest.java
  2010-05-15 15:08:25 UTC (rev 28878)
  @@ -0,0 +1,34 @@ 
 [...]
  +
 +public class WikiComponentManagerTest extends 
 AbstractComponentTestCase
  +{
 +    private WikiComponentManager manager;
 +
 +    @Before
 +    public void setUp() throws Exception
 +    {
 +        manager = 
 getComponentManager().lookup(WikiComponentManager.class);
  +    }
 +
 +    @Test
 +    public void testRegisterWikiComponent() throws Exception
 +    {
 +        DocumentReference pseudoReference = new 
DocumentReference("somewiki","XWiki","MyComponent");
  +
 +        DefaultWikiComponent wc = new 
 DefaultWikiComponent(pseudoReference,
WikiComponentManager.class,
 "test");
  +
 +        this.manager.registerWikiComponent(wc);
 +
 +        WikiComponentManager registered = 
this.getComponentManager().lookup(WikiComponentManager.class, "test");
  +
 +        Assert.assertNotNull(registered);
 +    }
 +} 
 --
 Thomas Mortagne
 _______________________________________________
 devs mailing list
 devs(a)xwiki.org
 
http://lists.xwiki.org/mailman/listinfo/devs  
_______________________________________________
 devs mailing list
 devs(a)xwiki.org