Changeset 4168


Ignore:
Timestamp:
Mar 4, 2008, 2:49:06 PM (14 years ago)
Author:
Nicklas Nordborg
Message:

References #436: Create extension system for the web application

Started with extension points for the menu and toolbars. Started with an web-based administration/information of installed extensions.

Location:
branches/extensions
Files:
24 added
8 deleted
18 edited

Legend:

Unmodified
Added
Removed
  • branches/extensions/config/dist/web.xml

    r3846 r4168  
    160160  </servlet-mapping>
    161161
     162  <!-- Extensions servlet for managing extensions to the web client -->
     163  <servlet>
     164    <servlet-name>ExtensionsServlet</servlet-name>
     165    <display-name>Web client extensions manager</display-name>
     166    <servlet-class>net.sf.basedb.clients.web.servlet.ExtensionsServlet</servlet-class>
     167    <load-on-startup>1</load-on-startup>
     168  </servlet>
     169  <servlet-mapping>
     170    <servlet-name>ExtensionsServlet</servlet-name>
     171    <url-pattern>/extensions</url-pattern>
     172  </servlet-mapping>
     173
    162174  <!-- The CompileAll servlet used to compile all JSP pages -->
    163175  <!-- EXPERIMENTAL!! -->
  • branches/extensions/src/clients/web/net/sf/basedb/clients/web/extensions/JspContext.java

    r4158 r4168  
    2424package net.sf.basedb.clients.web.extensions;
    2525
     26import java.io.IOException;
    2627import java.io.Writer;
     28import java.util.HashSet;
     29import java.util.Set;
     30
     31import javax.servlet.jsp.PageContext;
    2732
    2833import net.sf.basedb.core.SessionControl;
     
    3439  @version 2.7
    3540  @base.modified $Date$
    36  */
     41*/
    3742public class JspContext
    3843  extends Context
    3944{
     45 
     46  private Set<String> scripts;
     47  private Set<String> stylesheets;
     48 
    4049  private Writer out;
     50  private PageContext pageContext;
    4151 
    4252  public JspContext(SessionControl sc)
     
    5565  }
    5666 
     67  public void addScript(String path)
     68  {
     69    if (scripts == null) scripts = new HashSet<String>();
     70    scripts.add(path);
     71  }
     72 
     73  public void addStylesheet(String path)
     74  {
     75    if (stylesheets == null) stylesheets = new HashSet<String>();
     76    stylesheets.add(path);
     77  }
     78 
     79  public void writeScripts(String root)
     80    throws IOException
     81  {
     82    if (scripts == null) return;
     83    for (String script : scripts)
     84    {
     85      out.write("<script language=\"JavaScript\" type=\"text/javascript\" src=\"");
     86      out.write(root);
     87      out.write(script);
     88      out.write("\"></script>\n");
     89    }
     90  }
     91 
    5792}
  • branches/extensions/src/clients/web/net/sf/basedb/clients/web/extensions/MevActionButtonFactory.java

    r4158 r4168  
    2424package net.sf.basedb.clients.web.extensions;
    2525
     26import net.sf.basedb.clients.web.extensions.toolbar.ButtonAction;
     27import net.sf.basedb.clients.web.extensions.toolbar.ButtonBean;
    2628import net.sf.basedb.core.BioAssaySet;
    2729import net.sf.basedb.util.extensions.AbstractActionFactory;
     
    3537 */
    3638public class MevActionButtonFactory
    37   extends AbstractActionFactory<ActionButton>
     39  extends AbstractActionFactory<ButtonAction>
    3840{
    3941
    40   private String image;
     42  private String icon;
    4143  private String title;
    4244  private String onClick;
    4345 
    44   public ActionButton[] getActions(Context context)
     46  public boolean prepareContext(Context context)
     47  {
     48    JspContext jspContext = (JspContext)context;
     49    jspContext.addScript("mev_script.js");
     50    return true;
     51  }
     52 
     53  public ButtonAction[] getActions(Context context)
    4554  {
    4655    Object item = context.getCurrentItem();
    47     ActionButton action = null;
     56    ButtonBean action = null;
    4857    if (item instanceof BioAssaySet)
    4958    {
    5059      BioAssaySet bas = (BioAssaySet)item;
    51       action = new ImmutableActionButton(image, title, onClick + "(" + bas.getId() + ")");
     60      action = new ButtonBean();
     61      action.setIcon(icon);
     62      action.setTitle(title);
     63      action.setOnClick(onClick + "(" + bas.getId() + ")");
     64     
    5265    }
    53     return action == null ? null : new ActionButton[]{action};
     66    return action == null ? null : new ButtonAction[]{action};
    5467  }
    5568 
    56   public void setImage(String image)
     69  public void setIcon(String icon)
    5770  {
    58     this.image = image;
     71    this.icon = icon;
    5972  }
    6073  public void setTitle(String title)
  • branches/extensions/src/clients/web/net/sf/basedb/clients/web/extensions/WebExtensions.java

    r4163 r4168  
    2424package net.sf.basedb.clients.web.extensions;
    2525
    26 import java.io.InputStream;
    27 import java.net.URL;
    28 import java.util.List;
    2926
    30 import net.sf.basedb.util.extensions.Action;
    31 import net.sf.basedb.util.extensions.XmlLoader;
    32 import net.sf.basedb.util.extensions.ExtensionBean;
    33 import net.sf.basedb.util.extensions.ExtensionPoint;
    34 import net.sf.basedb.util.extensions.ExtensionPointBean;
     27import net.sf.basedb.util.extensions.Extension;
     28import net.sf.basedb.util.extensions.ExtensionsInvoker;
    3529import net.sf.basedb.util.extensions.Registry;
    3630
     
    4438{
    4539
    46   public static final Registry REGISTRY = new Registry();
     40  public static Registry REGISTRY = new Registry();
    4741 
    48   static
     42  public static Settings SETTINGS;
     43 
     44 
     45  public static ExtensionsInvoker<?> useExtensions(JspContext context, String...extensionPoints)
    4946  {
    50     // Register core extension points
    51     try
    52     {
    53       URL coreExtensionPoints =
    54         WebExtensions.class.getResource("/net/sf/basedb/clients/web/extensions/core-extension-points.xml");
    55       XmlLoader loader = new XmlLoader();
    56       loader.loadXmlFile(coreExtensionPoints.openStream(), coreExtensionPoints.toString(), null);
    57       loader.registerExtensionPoints(REGISTRY);
    58     }
    59     catch (Exception ex)
    60     {
    61       throw new RuntimeException(ex);
    62     }
    63 
    64     // Launch MeV button
    65     MevActionButtonFactory mevButtonFactory = new MevActionButtonFactory();
    66     mevButtonFactory.setImage("../../../images/tm4.png");
    67     mevButtonFactory.setTitle("MeV: MultiExperiment Viewer");
    68     mevButtonFactory.setOnClick("net_sf_launchMeV");
    69 
    70     ExtensionBean<ActionButton> mev = new ExtensionBean<ActionButton>();
    71     mev.setId("net.sf.basedb.clients.web.bioassayset.list.tools.mev");
    72     mev.setExtends("net.sf.basedb.clients.web.bioassayset.list.tools");
    73     mev.setActionFactory(mevButtonFactory);
    74     REGISTRY.registerExtension(mev);
    75 
    76     // MAGE Export button
    77     ImmutableActionButtonFactory otherButtonFactory = new ImmutableActionButtonFactory();
    78     otherButtonFactory.setImage("../../../images/decompress.png");
    79     otherButtonFactory.setTitle("MAGE Export");
    80     otherButtonFactory.setOnClick("alert('not yet implemented');");
    81    
    82     ExtensionBean<ActionButton> mage = new ExtensionBean<ActionButton>();
    83     mage.setId("net.sf.basedb.clients.web.bioassayset.list.tools.mage");
    84     mage.setExtends("net.sf.basedb.clients.web.bioassayset.list.tools");
    85     mage.setActionFactory(otherButtonFactory);
    86     REGISTRY.registerExtension(mage);
     47    return REGISTRY.useExtensions(context, SETTINGS, extensionPoints);
    8748  }
    8849 
    89 
     50  public static String getRootUrl(Extension extension)
     51  {
     52    return "/extensions/" + extension.getId();
     53  }
     54 
     55  public static String makeAbsolutePath(String path, Extension extension)
     56  {
     57    if (path != null && path.startsWith("~"))
     58    {
     59      path = getRootUrl(extension) + path.substring(1);
     60    }
     61    return path;
     62  }
    9063 
    9164}
  • branches/extensions/src/clients/web/net/sf/basedb/clients/web/extensions/core-extension-points.xml

    r4163 r4168  
    2727  >
    2828 
     29  <extension-point
     30    id="net.sf.basedb.clients.web.menu.extensions"
     31    >
     32    <action-class>net.sf.basedb.clients.web.extensions.menu.MenuItemAction</action-class>
     33    <name>Menu: extensions</name>
     34    <description>
     35      Extension point for adding extensions to the 'Extensions' menu.
     36      Extensions should provide MenuItemAction instances. The rendering
     37      is internal and extensions can't use their own rendering factories.
     38      The context will only include information about the currently logged
     39      in user, not information about the current page that is displayed.
     40      The reason for this is that the rendered menu is cached as a string
     41      in the user session. The menu is not updated on every page request.
     42      This extension point doesn't support custom stylesheets or javascripts.
     43    </description>
     44  </extension-point>
     45 
    2946  <extension-point
    3047    id="net.sf.basedb.clients.web.bioassayset.list.tools"
    3148    >
    32     <action-class>net.sf.basedb.clients.web.extensions.ActionButton</action-class>
     49    <action-class>net.sf.basedb.clients.web.extensions.toolbar.ButtonAction</action-class>
    3350    <renderer-factory override="true">
    34       <factory-class>net.sf.basedb.clients.web.extensions.ActionButtonRendererFactory</factory-class>
     51      <factory-class>net.sf.basedb.clients.web.extensions.toolbar.CompactButtonRendererFactory</factory-class>
    3552    </renderer-factory>
     53    <name>Bioassay set: Tools</name>
    3654    <description>
    3755      Extension point for the Tools column in the bioassayset tree
    3856      view of an experiment. Extensions should provide ActionButton
    3957      instances. The default renderer factory is ActionButtonRendererFactory,
    40       but this can be overridden by the extensions.
     58      but this can be overridden by the extensions. Note that the tree
     59      contains items of more than one type: BioAssaySet, Transformation and
     60      ExtraValue. The JspContext.getItem() can return any one of those
     61      types.
    4162    </description>
    4263  </extension-point>
  • branches/extensions/src/clients/web/net/sf/basedb/clients/web/taglib/Head.java

    r4093 r4168  
    148148  private void appendScripts(StringBuilder sb)
    149149  {
     150    SessionControl sc = page.getSessionControl();
    150151    LinkedHashSet<String> allSscripts = new LinkedHashSet<String>();
    151152    allSscripts.add("main.js");
     
    164165    sb.append("\t\tfunction getScale()\n");
    165166    sb.append("\t\t{\n");
    166     sb.append("\t\t\treturn ").append(Base.getScale(page.getSessionControl())).append(";\n");
     167    sb.append("\t\t\treturn ").append(Base.getScale(sc)).append(";\n");
     168    sb.append("\t\t}\n");
     169    sb.append("\t\tfunction getSessionId()\n");
     170    sb.append("\t\t{\n");
     171    sb.append("\t\t\treturn '").append(sc == null ? "" : sc.getId()).append("';\n");
    167172    sb.append("\t\t}\n");
    168173    sb.append("\t</script>\n");
  • branches/extensions/src/clients/web/net/sf/basedb/clients/web/taglib/menu/Menuitem.java

    r4093 r4168  
    270270    {
    271271      String theIcon = getIcon() == null ? "padding.gif" : getIcon();
    272       sb.append("<img src=\"").append(menu.getRoot()).append("images/").append(theIcon).append("\" alt=\"\" style=\"padding-left: 4px; padding-right: 4px;\">");
     272      if (theIcon.startsWith("/"))
     273      {
     274        // Remove starting / to avoid // in the src attribute
     275        theIcon = theIcon.substring(1);
     276      }
     277      else
     278      {
     279        // The icon is in the 'images' directory
     280        theIcon = "images/" + theIcon;
     281      }
     282      sb.append("<img src=\"").append(menu.getRoot()).append(theIcon).append("\" alt=\"\" style=\"padding-left: 4px; padding-right: 4px;\">");
    273283    }
    274284    String padding = menu.isVertical() ? "&nbsp;" : "";
  • branches/extensions/src/core/net/sf/basedb/core/Presets.java

    r3675 r4168  
    3131import java.util.Map;
    3232import java.util.TreeMap;
     33import java.io.InputStream;
     34import java.io.OutputStream;
    3335import java.net.URL;
    3436
    3537import org.jdom.Document;
    3638import org.jdom.Element;
     39import org.jdom.output.Format;
    3740import org.jdom.output.XMLOutputter;
    3841
     
    102105 
    103106  /**
     107    Load presets from a stream.
     108    @param in The stream to read from
     109    @param filename The original filename the stream is loaded
     110      from, or null if not known
     111    @since 2.7
     112  */
     113  public void loadFrom(InputStream in, String filename)
     114  {
     115    try
     116    {
     117      Document dom = XMLUtil.getValidatedXML(in, dtdFile, filename);
     118      loadPresets(dom);
     119    }
     120    catch (Exception ex)
     121    {
     122      throw new BaseException(ex);
     123    }
     124  }
     125 
     126  /**
     127    Write the prestes to a stream as an XML file.
     128    @param out The stream to write to.
     129    @since 2.7
     130  */
     131  public void writeTo(OutputStream out)
     132  {
     133    try
     134    {
     135      new XMLOutputter(Format.getPrettyFormat()).output(buildDocument(), out);
     136    }
     137    catch (Exception ex)
     138    {
     139      throw new BaseException(ex);
     140    }
     141  }
     142 
     143  /**
    104144    Convert the presets to an XML string.
    105145    @return The XML string
    106146  */
    107147  public String asXml()
     148  {
     149    return new XMLOutputter().outputString(buildDocument());
     150  }
     151
     152  /**
     153    Convert the presets to a XML document.
     154    @since 2.7
     155  */
     156  private Document buildDocument()
    108157  {
    109158    Document dom = XMLUtil.createDom("presets", "presets.dtd");
     
    121170      root.addContent(el);
    122171    }
    123     return new XMLOutputter().outputString(dom);
    124   }
    125 
     172    return dom;
     173  }
     174 
    126175  /**
    127176    Get the default preset. If no default exists a new default
  • branches/extensions/src/core/net/sf/basedb/core/xsd/extensions.xsd

    r4163 r4168  
    4545                </xsd:complexType>
    4646              </xsd:element>
     47              <xsd:element name="name" type="xsd:string" minOccurs="0" />
    4748              <xsd:element name="description" type="xsd:string" minOccurs="0" />
    4849            </xsd:all>
     
    5758              <xsd:element name="renderer-factory" type="factoryType" minOccurs="0" />
    5859              <xsd:element name="about" type="aboutType" minOccurs="0" />
    59               <xsd:element name="index" type="xsd:float" />
     60              <xsd:element name="index" type="xsd:float" minOccurs="0" />
    6061            </xsd:all>
    6162            <xsd:attribute name="id" type="xsd:ID" use="required" />
  • branches/extensions/src/core/net/sf/basedb/util/extensions/Context.java

    r4158 r4168  
    2424package net.sf.basedb.util.extensions;
    2525
     26import java.util.HashMap;
     27import java.util.Map;
     28
    2629import net.sf.basedb.core.SessionControl;
    2730
    2831/**
     32  Keeps information about the current context that
     33  extensions can use to decide what actions to generate.
     34  It is, for example, possible to check the permissions
     35  of logged in user and enable/disable certain actions
     36  if a condition is not met. Or, the extension may inspect
     37  the current item and decide if the extension can be used
     38  on that item or not.
     39 
     40  <p>
     41  The current context is passed to an {@link ActionFactory} twice, first
     42  to check if an extension should be enabled or not
     43  with the {@link ActionFactory#prepareContext(Context)}
     44  method. This method also allows an extension to write
     45  back information to the context. This may, for example,
     46  include information about which Javascript scripts to import
     47  or which stylesheets to load. The <code>prepareContext</code>
     48  method is only called once for each use of an extension.
     49
     50  <p>
     51  The second time is when calling the {@link
     52  ActionFactory#getActions(Context)} method. This method can
     53  be called multiple times. For example, if an extension point
     54  is in a list context, the <code>getActions</code> method may
     55  be called one time for each item in the list. If so, the
     56  {@link #getCurrentItem()} returns the currently active item.
     57 
     58  <p>
     59  Client applications may subclass this object to provide more
     60  information about the client environment. See, for example,
     61  the {@link net.sf.basedb.clients.web.extensions.JspContext} object.
    2962
    3063  @author nicklas
    3164  @version 2.7
    3265  @base.modified $Date$
    33  */
     66*/
    3467public class Context
    3568{
     
    3770  private final SessionControl sc;
    3871  private Object item;
     72  private Map<String, Object> attributes;
    3973 
     74  /**
     75    Create a new context.
     76    @param sc The current session
     77  */
    4078  public Context(SessionControl sc)
    4179  {
     
    4381  }
    4482 
     83  /**
     84    Create a new context.
     85    @param sc The current session
     86    @param item The current item
     87  */
     88  public Context(SessionControl sc, Object item)
     89  {
     90    this.sc = sc;
     91    this.item = item;
     92  }
     93
     94  /**
     95    Get the current session control.
     96  */
     97  public SessionControl getSessionControl()
     98  {
     99    return sc;
     100  }
     101
     102  /**
     103    Get the currently active item. It can be any type of
     104    object, but the extension point may declare (in it's
     105    documentation) that the current item is always of a
     106    certain type.
     107    @return The current item, or null
     108  */
     109  public Object getCurrentItem()
     110  {
     111    return item;
     112  }
     113 
     114  /**
     115    Set the currently active item.
     116    @param item The item that is the current item
     117  */
    45118  public void setCurrentItem(Object item)
    46119  {
     
    48121  }
    49122 
    50   public ResourceManager getResourceManager()
     123  /**
     124    Get an attribute of the context.
     125    @param name The name of the attribute
     126    @return The value, or null
     127  */
     128  public Object getAttribute(String name)
    51129  {
    52     return null;
     130    return attributes == null ? null : attributes.get(name);
    53131  }
    54132 
    55   public Object getCurrentItem()
     133  /**
     134    Set an attribute of the context.
     135    @param name The name of the attribute
     136    @param value The value, if null the attribute is removed
     137  */
     138  public void setAttribute(String name, Object value)
    56139  {
    57     return item;
     140    if (attributes == null) attributes = new HashMap<String, Object>();
     141    if (value != null)
     142    {
     143      attributes.put(name, value);
     144    }
     145    else
     146    {
     147      attributes.remove(name);
     148    }
    58149  }
    59150 
    60   public SessionControl getSessionControl()
    61   {
    62     return sc;
    63   }
    64  
    65  
    66151}
  • branches/extensions/src/core/net/sf/basedb/util/extensions/ExtensionPoint.java

    r4163 r4168  
    6666 
    6767  /**
     68    Get the name of the extension point. This is just for display
     69    purposes and the value is optional. If no name is given the
     70    ID can be used instead.
     71  */
     72  public String getName();
     73 
     74  /**
    6875    Get a description of the extension point. This value is optional
    6976    but we recommend that the description contains a text documenting
  • branches/extensions/src/core/net/sf/basedb/util/extensions/ExtensionPointBean.java

    r4163 r4168  
    3838
    3939  private String id;
     40  private String name;
    4041  private String description;
    4142  private Class<A> actionClass;
     
    5859  public ExtensionPointBean(
    5960    String id,
     61    String name,
    6062    String description,
    6163    Class<A> actionClass,
     
    6466  {
    6567    this.id = id;
     68    this.name = name;
    6669    this.description = description;
    6770    this.actionClass = actionClass;
     
    7881  {
    7982    this.id = id;
     83  }
     84 
     85  @Override
     86  public String getName()
     87  {
     88    return name;
     89  }
     90  public void setName(String name)
     91  {
     92    this.name = name;
    8093  }
    8194 
  • branches/extensions/src/core/net/sf/basedb/util/extensions/ExtensionsInvoker.java

    r4163 r4168  
    2525
    2626import java.util.Collection;
    27 import java.util.IdentityHashMap;
    2827import java.util.Iterator;
    29 import java.util.Map;
    30 import java.util.NoSuchElementException;
    3128
    3229import net.sf.basedb.util.extensions.Registry.RegisteredExtension;
    33 import net.sf.basedb.util.extensions.Registry.RegisteredExtensionPoint;
     30
    3431
    3532/**
     
    6158{
    6259
    63   private final Collection<RegisteredExtension<A>> extensions;
    64   private final Context context;
     60  final Collection<RegisteredExtension<A>> extensions;
     61  final Context context;
    6562  private RendererFactory<A> currentRenderFactory;
    6663 
     
    6966    this.extensions = extensions;
    7067    this.context = context;
    71     System.out.println("invoker: extensions=" + extensions);
    7268  }
    7369 
     
    7874    @return An iterator
    7975  */
    80   public Iterator<A> iterate()
     76  public ActionIterator<A> iterate()
    8177  {
    82     return new ActionIterator();
     78    return new ActionIterator<A>(context, extensions.iterator());
    8379  }
    8480 
     
    9288  public void renderDefault()
    9389  {
    94     ActionIterator it = new ActionIterator();
     90    ActionIterator<A> it = iterate();
    9591    while (it.hasNext())
    9692    {
     
    121117    }
    122118  }
    123  
    124   /**
    125     Internal iterator over all actions. The starting point is to
    126     iterate over all extensions. Each extension may return
    127     more than one action.
    128   */
    129   private class ActionIterator
    130     implements Iterator<A>
    131   {
    132     private final Iterator<RegisteredExtension<A>> iterator;
    133    
    134     // The actions created by the current extension
    135     private A[] actions;
    136     // The iterator's offset in the actions array
    137     private int offset;
    138 
    139     // The currently active extension
    140     private RegisteredExtension<A> currentExtension;
    141    
    142     // The renderer for the current action
    143     private Renderer<A> currentRenderer;
    144    
    145     // Cache for created renderers
    146     private Map<Object, Renderer<A>> rendererCache;
    147    
    148     // Boolean flags to indicate if we have a next element and if
    149     // we need to check or not. If both are 'false' we have reached the end
    150     private boolean checkNext;
    151     private boolean hasNext;
    152    
    153    
    154     ActionIterator()
    155     {
    156       this.iterator = extensions.iterator();
    157       this.checkNext = true;
    158       this.rendererCache = new IdentityHashMap<Object, Renderer<A>>();
    159     }
    160    
    161     public boolean hasNext()
    162     {
    163       if (checkNext)
    164       {
    165         checkNext = false;
    166         offset++;
    167         if (actions != null && offset < actions.length)
    168         {
    169           // There are more actions in the array
    170           hasNext = true;
    171         }
    172         else
    173         {
    174           // No more actions in the array, move on to the next extension
    175           currentExtension = null;
    176           currentRenderer = null;
    177           actions = null;
    178           offset = 0;
    179           hasNext = false;
    180          
    181           // Continue until we find an extension that creates at least one
    182           // action, or until we run out of extensions
    183           while (iterator.hasNext())
    184           {
    185             currentExtension = iterator.next();
    186             System.out.println("invoker: currentExtension=" + currentExtension);
    187 
    188             // Get the actions for the extension
    189             actions = currentExtension.getActionFactory().getActions(context);
    190             System.out.println("invoker: actions=" + actions);
    191             if (actions != null && actions.length > 0)
    192             {
    193               System.out.println("invoker: actions=" + actions.length);
    194               hasNext = true;
    195               break;
    196             }
    197           }
    198         }
    199       }
    200       return hasNext;
    201     }
    202    
    203     public A next()
    204     {
    205       if (checkNext) hasNext();
    206       if (!hasNext) throw new NoSuchElementException();
    207       checkNext = true;
    208       return actions[offset];
    209     }
    210    
    211     /**
    212       Not supported.
    213       @throws UnsupportedOperationException Always
    214     */
    215     public void remove()
    216     {
    217       throw new UnsupportedOperationException();
    218     }
    219    
    220     /**
    221       Get the extension that created the current action.
    222     */
    223     RegisteredExtension<A> getExtension()
    224     {
    225       return currentExtension;
    226     }
    227    
    228     /**
    229       Gets the renderer for the current action.
    230     */
    231     Renderer<A> getRenderer()
    232     {
    233       if (currentRenderer == null)
    234       {
    235         RegisteredExtensionPoint<A> ep = currentExtension.getExtensionPoint();
    236         RendererFactory<A> rf = currentExtension.getRendererFactory();
    237         if (ep.allowRendererOverride() && rf != null)
    238         {
    239           // Use the extension's renderer factory if it is
    240           // allowed and if one is provided
    241           currentRenderer = rf.getRenderer(context);
    242         }
    243         else
    244         {
    245           // We must use the extensions points renderer factory
    246           // First, check the cache if we already have a renderer
    247           currentRenderer = rendererCache.get(ep);
    248           if (currentRenderer == null)
    249           {
    250             currentRenderer = ep.getRendererFactory().getRenderer(context);
    251             rendererCache.put(ep, currentRenderer);
    252           }
    253         }
    254       }
    255       return currentRenderer;
    256     }
    257   }
    258119}
  • branches/extensions/src/core/net/sf/basedb/util/extensions/Registry.java

    r4163 r4168  
    2727import java.util.Collection;
    2828import java.util.Collections;
    29 import java.util.Comparator;
    3029import java.util.HashMap;
    3130import java.util.Iterator;
     
    164163    // Create a copy, or the iterator will fail if new
    165164    // extension points are registered by another thread
    166     List<ExtensionPoint<?>> copy = new ArrayList<ExtensionPoint<?>>(extensionPoints.values());
     165    List<ExtensionPoint<?>> copy =
     166      new ArrayList<ExtensionPoint<?>>(extensionPoints.values());
    167167    return Collections.unmodifiableList(copy).iterator();
    168168  }
     
    255255    @param id The ID of the extension point
    256256    @return An {@link Extension} object or null if that
    257       extension point doesn't exists
     257      extension doesn't exists
    258258  */
    259259  public Extension<?> getExtension(String id)
     
    273273    // Create a copy, or the iterator will fail if new
    274274    // extension points are registered by another thread
    275     List<Extension<?>> copy = new ArrayList<Extension<?>>(extensions.values());
     275    List<Extension<?>> copy =
     276      new ArrayList<Extension<?>>(extensions.values());
    276277    return Collections.unmodifiableList(copy).iterator();
    277278  }
     
    299300      // Create a copy, or the iterator will fail if new
    300301      // extension points are registered by another thread
    301       List<Extension<?>> copy = new ArrayList<Extension<?>>(rep.getExtensions());
     302      List<Extension<?>> copy =
     303        new ArrayList<Extension<?>>(rep.getExtensions());
     304      Collections.sort(copy, DefaultFilter.INDEX_COMPARATOR);
    302305      return Collections.unmodifiableList(copy).iterator();
    303306    }
     
    326329   
    327330    @param context Information about the current context
     331    @param filter A filter object that can be used to filter out
     332      disabled extensions and sort the extensions in a particular order.
     333      If this value is null, no extensions are filtered out and they
     334      are sorted according to their {@link Extension#getIndex()} value
    328335    @param extensionPoints An array of extension point ID:s to get
    329336      extension for. In most cases the extension points should
     
    333340  */
    334341  @SuppressWarnings("unchecked")
    335   public ExtensionsInvoker<?> useExtensions(Context context, String... extensionPoints)
    336   {
     342  public ExtensionsInvoker<?> useExtensions(Context context, ExtensionsFilter filter, String... extensionPoints)
     343  {
     344    if (filter == null) filter = DEFAULT_FILTER;
    337345    List<RegisteredExtension<Action>> use = new LinkedList<RegisteredExtension<Action>>();
    338346    for (String id : extensionPoints)
    339347    {
    340       RegisteredExtensionPoint<Action> rep = (RegisteredExtensionPoint<Action>)this.extensionPoints.get(id);
    341       if (rep != null)
     348      RegisteredExtensionPoint<Action> rep =
     349        (RegisteredExtensionPoint<Action>)this.extensionPoints.get(id);
     350      if (rep != null && filter.isEnabled(rep))
    342351      {
    343352        for (RegisteredExtension<Action> ext : rep.getExtensions())
    344353        {
    345           if (ext.getActionFactory().prepareContext(context))
     354          if (filter.isEnabled(ext) && ext.getActionFactory().prepareContext(context))
    346355          {
    347356            use.add(ext);
     
    350359      }
    351360    }
    352     Collections.sort(use, INDEX_COMPARATOR);
     361    filter.sort(use);
    353362    return new ExtensionsInvoker<Action>(use, context);
    354363  }
    355364 
    356365  /**
    357     Comparator used to order extensions by their index value.
    358     @see Extension#getIndex()
    359    */
    360   private static final Comparator<Extension> INDEX_COMPARATOR =
    361     new Comparator<Extension>()
    362     {
    363       @Override
    364       public int compare(Extension o1, Extension o2)
    365       {
    366         return Float.compare(o1.getIndex(), o2.getIndex());
    367       }
    368     };
    369  
    370  
     366    Default filter implementation for
     367    {@link #useExtensions(Context, ExtensionsFilter, String...)}
     368  */
     369  private static final ExtensionsFilter DEFAULT_FILTER = new DefaultFilter();
     370   
    371371  /**
    372372    Internal representation of an extension point.
     
    378378    private final String id;
    379379    private final Class<A> actionClass;
     380    private String name;
    380381    private String description;
    381382    private RendererFactory<A> rendererFactory;
     
    391392    {
    392393      this.id = ep.getId();
     394      this.name = ep.getName();
    393395      this.description = ep.getDescription();
    394396      this.actionClass = ep.getActionClass();
     
    409411
    410412    @Override
     413    public String getName()
     414    {
     415      return name;
     416    }
     417
     418    @Override
    411419    public String getDescription()
    412420    {
     
    436444    public String toString()
    437445    {
    438       return "ExtensionPoint[id="+ id +"]";
     446      return "ExtensionPoint[id="+ id +"; name=" + name + "]";
    439447    }
    440448   
     
    444452    void update(ExtensionPoint<A> ep)
    445453    {
     454      this.name = ep.getName();
    446455      this.description = ep.getDescription();
    447456      this.rendererFactory = ep.getRendererFactory();
     
    471480    {
    472481      return this.extensions.values();
    473     }
    474 
    475   }
    476  
     482    }
     483  }
     484   
    477485  /**
    478486    Internal representation of an extension.
     
    500508      this.extensionPoint = extension.getExtends();
    501509      this.index = extension.getIndex();
    502       this.about = extension.getAbout() == null ? null : new RegisteredAbout(about);
     510      this.about = extension.getAbout() == null ?
     511        null : new RegisteredAbout(extension.getAbout());
    503512      this.actionFactory = extension.getActionFactory();
    504513      this.rendererFactory = extension.getRendererFactory();
     
    551560      return "Extension[id="+ id +"]";
    552561    }
    553 
     562   
    554563    /**
    555564      Update the registered information.
     
    584593      return rep;
    585594    }
    586   }
    587  
     595  }   
     596   
    588597  static class RegisteredAbout
    589598    implements About
  • branches/extensions/src/core/net/sf/basedb/util/extensions/XmlLoader.java

    r4163 r4168  
    196196        id="..."
    197197        >
     198        <name>...</name>
    198199        <description>...</description>
    199200        <action-class>...</action-class>
     
    218219      extensionPoint.setId(id);
    219220     
    220       // Set Description (optional)
     221      // Set Name and Description (optional)
     222      extensionPoint.setName(Values.getStringOrNull(epTag.getChildText("name", ns)));
    221223      extensionPoint.setDescription(Values.getStringOrNull(epTag.getChildText("description", ns)));
    222224     
     
    295297      ext.setExtends(extensionPoint);
    296298     
    297       // Set index position (required)
    298       ext.setIndex(Values.getFloat(epTag.getChildText("index", ns)));
     299      // Set index position (optional, default value = 999)
     300      ext.setIndex(Values.getFloat(epTag.getChildText("index", ns), 999.0f));
    299301     
    300302      // Set About (optional)
     
    303305      {
    304306        AboutBean about = new AboutBean();
     307        ext.setAbout(about);
    305308        about.setName(Values.getStringOrNull(aboutTag.getChildText("name", ns)));
    306309        about.setDescription(Values.getStringOrNull(aboutTag.getChildText("description", ns)));
  • branches/extensions/www/include/menu.jsp

    r4022 r4168  
    5757  import="net.sf.basedb.clients.web.util.HTML"
    5858  import="net.sf.basedb.util.Values"
     59  import="net.sf.basedb.clients.web.extensions.WebExtensions"
     60  import="net.sf.basedb.clients.web.extensions.JspContext"
     61  import="net.sf.basedb.clients.web.extensions.menu.MenuItemAction"
     62  import="net.sf.basedb.util.extensions.Extension"
     63  import="net.sf.basedb.util.extensions.ExtensionsInvoker"
     64  import="net.sf.basedb.util.extensions.ActionIterator"
    5965  import="java.util.HashMap"
    6066  import="java.util.ArrayList"
    6167  import="java.util.List"
    6268  import="java.util.Arrays"
     69  import="java.util.Iterator"
    6370%>
    6471<%@ taglib prefix="m" uri="/WEB-INF/menu.tld" %>
     
    942949      />
    943950    </m:menu>
     951   
     952    <%
     953    // Extensions menu
     954    JspContext context = new JspContext(sc);
     955    ExtensionsInvoker<MenuItemAction> invoker =
     956      (ExtensionsInvoker<MenuItemAction>)WebExtensions.useExtensions(context,
     957        "net.sf.basedb.clients.web.menu.extensions");
     958    ActionIterator<MenuItemAction> items = invoker.iterate();
     959    boolean addSeparator = false;
     960    %>
     961    <m:menu id="extensions" style="display: none">
     962      <%
     963      while (items.hasNext())
     964      {
     965        MenuItemAction item = items.next();
     966        Extension extension = items.getExtension();
     967        if (item.getType() == MenuItemAction.MenuType.SEPARATOR)
     968        {
     969          addSeparator = false;
     970          %>
     971          <m:menuseparator style="<%=item.getStyle()%>" visible="<%=item.isVisible()%>" />
     972          <%
     973        }
     974        else if (item.getType() == MenuItemAction.MenuType.MENUITEM)
     975        {
     976          addSeparator = true;
     977          %>
     978          <m:menuitem
     979            style="<%=item.getStyle()%>"
     980            title="<%=item.getTitle()%>"
     981            icon="<%=WebExtensions.makeAbsolutePath(item.getIcon(), extension)%>"
     982            tooltip="<%=item.getTooltip()%>"
     983            enabled="<%=item.isEnabled()%>"
     984            visible="<%=item.isVisible()%>"
     985            onclick="<%=item.getOnClick()%>"
     986          />
     987          <%
     988        }
     989      }
     990      if (addSeparator)
     991      {
     992        %>
     993        <m:menuseparator />
     994        <%
     995      }
     996      %>
     997      <m:menuitem
     998        title="Reload"
     999        onclick="<%="Main.openPopup('"+root+"extensions?reload', 'Reload', 360, 200);"%>"
     1000      />
     1001      <m:menuitem
     1002        title="Installed extensions"
     1003        onclick="<%="Menu.openUrl('"+root+"admin/extensions/index.jsp?ID="+ID+"')"%>"
     1004      />
     1005    </m:menu>
     1006   
    9441007    <%
    9451008    // Main menu
     
    9721035      />
    9731036      <m:submenu
     1037        subid="extensions"
     1038        title="Extensions"
     1039      />
     1040      <m:submenu
    9741041        subid="help"
    9751042        title="Help"
  • branches/extensions/www/include/scripts/main.js

    r4003 r4168  
    11001100  {
    11011101    var url = getRoot()+'common/calendar.jsp?title='+escape(title)+'&form='+form+'&input='+input+'&format='+escape(format);
    1102     url += '&ID='+Main.getIdFromLocation();
     1102    url += '&ID='+getSessionId();
    11031103    if (callback) url += '&callback='+callback;
    11041104    Main.openPopup(url, title.replace(/[^\w]/, ''), 350, 240);
  • branches/extensions/www/views/experiments/bioassaysets/analysis_tree.jsp

    r4163 r4168  
    6363  import="net.sf.basedb.clients.web.formatter.FormatterFactory"
    6464  import="net.sf.basedb.clients.web.extensions.WebExtensions"
    65   import="net.sf.basedb.clients.web.extensions.ActionButton"
    66   import="net.sf.basedb.clients.web.extensions.ActionButtonRenderer"
     65  import="net.sf.basedb.clients.web.extensions.toolbar.ButtonAction"
    6766  import="net.sf.basedb.clients.web.extensions.JspContext"
    6867  import="net.sf.basedb.util.extensions.Context"
     
    237236final SessionControl sc = Base.getExistingSessionControl(pageContext, Permission.DENIED, itemType);
    238237final String ID = sc.getId();
     238final String rootPath = request.getContextPath()+"/";
    239239final ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, null, null);
    240240final ItemContext tc = sc.getCurrentContext(Item.TRANSFORMATION);
     
    281281 
    282282  JspContext jspContext = new JspContext(sc);
    283   ExtensionsInvoker<ActionButton> invoker =
    284     (ExtensionsInvoker<ActionButton>)WebExtensions.REGISTRY.useExtensions(jspContext, "net.sf.basedb.clients.web.bioassayset.list.tools");
     283  ExtensionsInvoker<ButtonAction> invoker =
     284    (ExtensionsInvoker<ButtonAction>)WebExtensions.useExtensions(jspContext, "net.sf.basedb.clients.web.bioassayset.list.tools");
    285285  %>
    286286<base:page type="include">
    287287  <base:body>
     288    <%
     289      jspContext.setOut(out);
     290      jspContext.writeScripts(rootPath);
     291    %>
    288292    <script language="JavaScript">
    289293    var submitPage = '<%=transformationId != 0 ? "../bioassaysets/index.jsp" : "index.jsp"%>';
Note: See TracChangeset for help on using the changeset viewer.