Changeset 4170


Ignore:
Timestamp:
Mar 7, 2008, 2:07:10 PM (14 years ago)
Author:
Nicklas Nordborg
Message:

References #436: Create extension system for the web application

Location:
branches/extensions
Files:
8 added
1 deleted
27 edited
1 moved

Legend:

Unmodified
Added
Removed
  • branches/extensions/src/clients/web/net/sf/basedb/clients/web/extensions/JspContext.java

    r4168 r4170  
    3232
    3333import net.sf.basedb.core.SessionControl;
     34import net.sf.basedb.util.extensions.ActionFactory;
    3435import net.sf.basedb.util.extensions.Context;
     36import net.sf.basedb.util.extensions.Extension;
     37import net.sf.basedb.util.extensions.RendererFactory;
    3538
    3639/**
     40  Context object for the web application. Instances of this class
     41  are always used for passing context information to web extensions.
     42  This means that it is always safe to cast the context to this
     43  class in the {@link ActionFactory#prepareContext(Context, Extension)}
     44  and {@link ActionFactory#getActions(Context, Extension)} methods
     45  and other methods that has a context parameter.
     46 
     47  <pre class="code">
     48@Override
     49public boolean prepareContext(Context context, Extension extension)
     50{
     51  JspContext jspContext = (JspContext)context;
     52  jspContext.addStylesheet("/base/include/styles/mystylesheet.css");
     53  // Do other things
     54  return true;
     55}
     56</pre>
     57
     58  <p>
     59  This context exposes the underlying JSP {@link PageContext},
     60  which can be used to get information about the current request
     61  among other things. Render factories usually needs access to
     62  the response output stream, {@link #getOut()}, so that renderers
     63  can write the generated HTML to the response.
    3764
    3865  @author nicklas
     
    6592  }
    6693 
    67   public void addScript(String path)
     94  /**
     95    Add an absolute reference to a script that should be included on the
     96    generated page with a <code>&lt;script&gt;</code> tag. The
     97    path must be an absolute path.
     98
     99    <p>
     100    Note 1! This method must be called from the {@link
     101    ActionFactory#prepareContext(Context, Extension)} or
     102    {@link RendererFactory#prepareContext(Context, Extension)}
     103    methods. Changes to the context after that will not be reflected
     104    in the output.
     105   
     106    <p>
     107    Note 2! Not all extension points supports custom scripts. Check
     108    the documentation for the extension point you are extending.
     109   
     110    @param absolutePath The absolute path
     111  */
     112  public void addScript(String absolutePath)
    68113  {
    69114    if (scripts == null) scripts = new HashSet<String>();
    70     scripts.add(path);
     115    scripts.add(absolutePath);
    71116  }
    72117 
    73   public void addStylesheet(String path)
     118  /**
     119    Add an absolute reference to a CSS stylesheet that should be included on the
     120    generated page with a <code>&lt;link&gt;</code> tag. The
     121    path must be the absolute path.
     122
     123    <p>
     124    Note 1! This method must be called from the {@link
     125    ActionFactory#prepareContext(Context, Extension)} or
     126    {@link RendererFactory#prepareContext(Context, Extension)}
     127    methods. Changes to the context after that will not be reflected
     128    in the output.
     129   
     130    <p>
     131    Note 2! Not all extension points supports custom stylesheets. Check
     132    the documentation for the extension point you are extending.
     133
     134    @param absolutePath The absolute path
     135  */
     136  public void addStylesheet(String absolutePath)
    74137  {
    75138    if (stylesheets == null) stylesheets = new HashSet<String>();
    76     stylesheets.add(path);
     139    stylesheets.add(absolutePath);
    77140  }
     141
    78142 
    79   public void writeScripts(String root)
     143  public void writeScripts()
    80144    throws IOException
    81145  {
     
    84148    {
    85149      out.write("<script language=\"JavaScript\" type=\"text/javascript\" src=\"");
    86       out.write(root);
    87150      out.write(script);
    88151      out.write("\"></script>\n");
  • branches/extensions/src/clients/web/net/sf/basedb/clients/web/extensions/MevActionButtonFactory.java

    r4168 r4170  
    2727import net.sf.basedb.clients.web.extensions.toolbar.ButtonBean;
    2828import net.sf.basedb.core.BioAssaySet;
    29 import net.sf.basedb.util.extensions.AbstractActionFactory;
    3029import net.sf.basedb.util.extensions.Context;
     30import net.sf.basedb.util.extensions.Extension;
     31import net.sf.basedb.util.extensions.xml.PathSetter;
     32import net.sf.basedb.util.extensions.xml.VariableSetter;
    3133
    3234/**
     
    3739 */
    3840public class MevActionButtonFactory
    39   extends AbstractActionFactory<ButtonAction>
     41  extends AbstractJspActionFactory<ButtonAction>
    4042{
    4143
     
    4446  private String onClick;
    4547 
    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)
     48  @Override
     49  public ButtonAction[] getActions(Context context, Extension extension)
    5450  {
    5551    Object item = context.getCurrentItem();
     
    6763  }
    6864 
     65  @PathSetter
     66  @VariableSetter
    6967  public void setIcon(String icon)
    7068  {
     
    7573    this.title = title;
    7674  }
     75  @VariableSetter
    7776  public void setOnClick(String onClick)
    7877  {
  • branches/extensions/src/clients/web/net/sf/basedb/clients/web/extensions/Settings.java

    r4168 r4170  
    3838import net.sf.basedb.util.extensions.ExtensionPoint;
    3939import net.sf.basedb.util.extensions.ExtensionsFilter;
     40import net.sf.basedb.util.extensions.Registry;
    4041
    4142/**
    4243  Keeps settings for the extensions system. Settings
    43   are kept in the file <code>WEB-INF/extensions/settings.xml</code>
    44  
     44  are kept in the file <code>WEB-INF/extensions/settings.xml</code>.
     45  <p>
     46
     47  Extension points and extensions can be disabled/enabled by
     48  {@link #enableExtensionPoint(String, boolean)} and
     49  {@link #enableExtension(String, boolean)}.
     50  <p>
     51  This class also implements the {@link ExtensionsFilter}
     52  interface, and can be used on the
     53  {@link Registry#useExtensions(net.sf.basedb.util.extensions.Context, ExtensionsFilter, String...)}
     54  method. This means that disabling/enabling extensions and extension points
     55  will immediately be visible in the web interface.
     56
     57  @author nicklas
     58  @version 2.7
     59  @base.modified $Date$
    4560*/
    4661public class Settings
     
    4964{
    5065
     66  private boolean hasChanged;
    5167  private Presets presets;
    5268  private Preset settings;
     
    7187    this.disabledExtensionPoints = presets.getPreset("disabled-extension-points");
    7288    this.disabledExtensions = presets.getPreset("disabled-extensions");
     89    hasChanged = false;
    7390  }
    7491 
     
    100117  {
    101118    disabledExtensions.setSetting(extensionId, enable ? null : "1");
     119    hasChanged = true;
    102120  }
    103121 
     
    110128  {
    111129    disabledExtensionPoints.setSetting(extensionPointId, enable ? null : "1");
     130    hasChanged = true;
    112131  }
    113132 
     133  /**
     134    Save the settings to disk. If the settings has not been
     135    changed the file is not written.
     136   
     137    @throws IOException
     138  */
    114139  public synchronized void save()
    115140    throws IOException
    116141  {
     142    if (!hasChanged) return;
    117143    OutputStream out = new FileOutputStream(filename);
    118144    presets.writeTo(out);
     145    out.close();
     146    hasChanged = true;
    119147  }
    120148}
  • branches/extensions/src/clients/web/net/sf/basedb/clients/web/extensions/WebExtensions.java

    r4168 r4170  
    4242  public static Settings SETTINGS;
    4343 
     44  public static String ROOT = "/extension";
    4445 
    4546  public static ExtensionsInvoker<?> useExtensions(JspContext context, String...extensionPoints)
     
    4849  }
    4950 
    50   public static String getRootUrl(Extension extension)
     51  public static String getRoot(Extension extension)
    5152  {
    52     return "/extensions/" + extension.getId();
     53    return ROOT + "/extensions/" + extension.getId();
    5354  }
    5455 
     56  /**
     57    Convert a path to an absolute path seen from the web
     58    servers perspective. Here are the rules:
     59    <ol>
     60    <li>If <code>path</code> starts with '/', the path is relative to
     61      the BASE application root directory, which is added to
     62      the beginning of the path, for example,
     63      <code>/images/copy.gif --&gt; /base/images/copy.gif</code>
     64
     65    <li>If <code>path</code> stars with '~', the path is relative to
     66      the extensions home directory. The home directory of an extension
     67      is normally located in the <code>/extensions</code> directory
     68      with a name based on the JAR file the extension is located in.
     69      For example,
     70      <code>~/images/tmev.png --&gt; /base/extensions/net.sf.basedb.mev/images/tmev.png</code>.
     71
     72    <li>Otherwise, the <code>path</code> is returned unmodified.
     73    </ol>
     74
     75    @param path The path to convert to an absolute path
     76    @param extension The extension that is requesting the path
     77      conversion
     78    @return The absolute path
     79  */
    5580  public static String makeAbsolutePath(String path, Extension extension)
    5681  {
    57     if (path != null && path.startsWith("~"))
     82    if (path != null)
    5883    {
    59       path = getRootUrl(extension) + path.substring(1);
     84      if (path.startsWith("/"))
     85      {
     86        path = ROOT + path;
     87      }
     88      else if (path.startsWith("~"))
     89      {
     90        path = getRoot(extension) + path.substring(1);
     91      }
    6092    }
    6193    return path;
  • branches/extensions/src/clients/web/net/sf/basedb/clients/web/extensions/menu/FixedMenuItemFactory.java

    r4168 r4170  
    2424package net.sf.basedb.clients.web.extensions.menu;
    2525
     26import net.sf.basedb.clients.web.extensions.AbstractJspActionFactory;
    2627import net.sf.basedb.util.Values;
    27 import net.sf.basedb.util.extensions.ActionFactory;
    2828import net.sf.basedb.util.extensions.Context;
     29import net.sf.basedb.util.extensions.Extension;
     30import net.sf.basedb.util.extensions.xml.PathSetter;
     31import net.sf.basedb.util.extensions.xml.VariableSetter;
    2932
    3033/**
    3134  A simple implementation of a menu item action factory that
    3235  creates the same fixed menu item for all users
    33   not using any context information.
     36  not using any context information. 
    3437  <p>
    3538  The menu is by default a regular menuitem and is both
     
    3740  the properties. Changes to the properties are immediately
    3841  visible in the menu items returned from the {@link #getActions(Context)}
    39   method.
     42  method. 
    4043 
    4144  @author nicklas
     
    4447*/
    4548public class FixedMenuItemFactory
    46   implements ActionFactory<MenuItemAction>, MenuItemAction
    47  
     49  extends AbstractJspActionFactory<MenuItemAction>
     50  implements MenuItemAction
    4851{
    4952
     
    7578  */
    7679  @Override
    77   public MenuItemAction[] getActions(Context context)
     80  public MenuItemAction[] getActions(Context context,
     81    Extension<? super MenuItemAction> extension)
    7882  {
    7983    return actions;
    80   }
    81 
    82   @Override
    83   public boolean prepareContext(Context context)
    84   {
    85     return true;
    8684  }
    8785  // ------------------------------------
     
    140138  // ------------------------------------
    141139
     140  /*
     141    Setter methods that are called when the factory is
     142    initialised.
     143  */
     144  @VariableSetter
     145  @PathSetter
    142146  public void setIcon(String icon)
    143147  {
     
    145149  }
    146150
     151  @VariableSetter
    147152  public void setOnClick(String onClick)
    148153  {
     
    194199    this.type = MenuType.valueOf(type);
    195200  }
     201  // ------------------------------------
    196202 
    197203  @Override
     
    208214    sb.append("; visible=" + visible);
    209215    sb.append("; enabled=" + enabled);
     216    sb.append(super.toString());
    210217    return sb.toString();
    211218  }
  • branches/extensions/src/clients/web/net/sf/basedb/clients/web/extensions/menu/MenuItemAction.java

    r4168 r4170  
    5757 
    5858  /**
    59     Get a reference to an image that will be displayed in the menu.
     59    Get an absolute reference to an image that will be displayed in the menu.
    6060    It is recommended that the image is 9 pixels wide and 12 pixels high to
    61     line up with the icons used by the BASE core menus. If the image
    62     URL starts with:
    63     <ul>
    64     <li><b>/</b> It is interpreted as relative to the
    65       root directory of the BASE application (normally, /base)
    66     <li><b>~</b> It is interpreted as relative to the resources directory
    67       for the current extension (normally, /base/extensions/ID)
    68     <li>Otherwise it is interpreted as relative to the 'images'
    69       subdirectory (/base/images).
    70     </ul>
     61    line up with the icons used by the BASE core menus.
    7162   
    7263    @return A reference to an image, or null if no image should
     
    119110Menu.openUrl(getRoot()+'views/reporters/index.jsp?ID='+getSessionId());
    120111</pre>
    121      
     112
     113      Another possibility that works for the root path is to
     114      use a factory that has enabled variable substitution. In this
     115      case you can use:
     116
     117      <pre class="code">
     118Menu.openUrl('$ROOT$/views/reporters/index.jsp?ID='+getSessionId());
     119</pre>
     120
    122121    <li>To open a popup window use:
    123122      <code>Main.openPopup(url, windowName, width, height, options)</code>
     
    131130      documentation for the <code>window.open()</code> javascript function
    132131      for valid syntax.
    133      
    134132    </ul>
    135133   
  • branches/extensions/src/clients/web/net/sf/basedb/clients/web/extensions/menu/PermissionMenuItemFactory.java

    r4168 r4170  
    2424package net.sf.basedb.clients.web.extensions.menu;
    2525
     26import net.sf.basedb.clients.web.extensions.AbstractJspActionFactory;
    2627import net.sf.basedb.clients.web.extensions.menu.MenuItemAction.MenuType;
    2728import net.sf.basedb.core.Item;
    2829import net.sf.basedb.core.Permission;
    2930import net.sf.basedb.core.SessionControl;
    30 import net.sf.basedb.util.extensions.ActionFactory;
    3131import net.sf.basedb.util.extensions.Context;
     32import net.sf.basedb.util.extensions.Extension;
     33import net.sf.basedb.util.extensions.xml.PathSetter;
     34import net.sf.basedb.util.extensions.xml.VariableSetter;
    3235
    3336/**
     
    6063*/
    6164public class PermissionMenuItemFactory
    62   implements ActionFactory<MenuItemAction>
     65  extends AbstractJspActionFactory<MenuItemAction>
    6366{
    6467
     
    98101  */
    99102  @Override
    100   public MenuItemAction[] getActions(Context context)
     103  public MenuItemAction[] getActions(Context context, Extension<? super MenuItemAction> extension)
    101104  {
    102105    SessionControl sc = context.getSessionControl();
     
    120123    }
    121124    return new MenuItemAction[] { action };
    122    
    123125  }
    124126
    125127  @Override
    126   public boolean prepareContext(Context context)
     128  public boolean prepareContext(Context context, Extension<? super MenuItemAction> extension)
    127129  {
    128130    SessionControl sc = context.getSessionControl();
    129     return hasPermission(sc, visiblePermission);
     131    boolean isEnabled = hasPermission(sc, visiblePermission);
     132    return isEnabled && super.prepareContext(context, extension);
    130133  }
    131134  // ------------------------------------
     
    163166    Set the icon to use when the menu is enabled.
    164167  */
     168  @VariableSetter
     169  @PathSetter
    165170  public void setEnabledIcon(String icon)
    166171  {
     
    170175    Set the icon to use when the menu is disabled.
    171176  */
     177  @VariableSetter
     178  @PathSetter
    172179  public void setDisabledIcon(String icon)
    173180  {
     
    177184    Set the icon to use in all cases.
    178185  */
     186  @VariableSetter
     187  @PathSetter
    179188  public void setIcon(String icon)
    180189  {
     
    186195    Set the javascript action to call when the menu is clicked.
    187196  */
     197  @VariableSetter
    188198  public void setOnClick(String onClick)
    189199  {
  • branches/extensions/src/clients/web/net/sf/basedb/clients/web/extensions/toolbar/CompactButtonRendererFactory.java

    r4168 r4170  
    2626import net.sf.basedb.clients.web.extensions.JspContext;
    2727import net.sf.basedb.util.extensions.Context;
     28import net.sf.basedb.util.extensions.Extension;
    2829import net.sf.basedb.util.extensions.RendererFactory;
    2930
     
    3839{
    3940
     41  public void prepareContext(Context context, Extension extension)
     42  {
     43   
     44  }
    4045 
    41   public CompactButtonRenderer getRenderer(Context context)
     46  public CompactButtonRenderer getRenderer(Context context, Extension extension)
    4247  {
    4348    return new CompactButtonRenderer((JspContext)context);
  • branches/extensions/src/clients/web/net/sf/basedb/clients/web/extensions/toolbar/FixedButtonFactory.java

    r4168 r4170  
    2424package net.sf.basedb.clients.web.extensions.toolbar;
    2525
     26import net.sf.basedb.clients.web.extensions.AbstractJspActionFactory;
    2627import net.sf.basedb.util.Values;
    27 import net.sf.basedb.util.extensions.ActionFactory;
    2828import net.sf.basedb.util.extensions.Context;
     29import net.sf.basedb.util.extensions.Extension;
     30import net.sf.basedb.util.extensions.xml.PathSetter;
     31import net.sf.basedb.util.extensions.xml.VariableSetter;
    2932
    3033/**
     
    3336  not using any context information.
    3437  <p>
    35   The button is by default both
    36   visible and enabled. Use the setter method to change
    37   the properties. Changes to the properties are immediately
     38  The button is by default both visible and enabled. Use the setter
     39  method to change the properties. Changes to the properties are immediately
    3840  visible in the menu items returned from the {@link #getActions(Context)}
    3941  method.
     42  <p>
     43  If the extension point supports scripts and stylesheets, use the
     44  {@link #setScript(String)} and {@link #setStylesheet(String)} methods.
     45  Each call to these methods will add the the argument to a set. Use
     46  the {@link #getScripts()} and {@link #getStylesheets()} methods to gain
     47  access to the sets, for example, to remove added items.
    4048 
    4149  @author nicklas
     
    4452*/
    4553public class FixedButtonFactory
    46   implements ActionFactory<ButtonAction>, ButtonAction
     54  extends AbstractJspActionFactory<ButtonAction>
     55  implements ButtonAction
    4756 
    4857{
     
    7584  */
    7685  @Override
    77   public ButtonAction[] getActions(Context context)
     86  public ButtonAction[] getActions(Context context, Extension extension)
    7887  {
    7988    return actions;
    80   }
    81 
    82   @Override
    83   public boolean prepareContext(Context context)
    84   {
    85     return true;
    8689  }
    8790  // ------------------------------------
     
    151154  }
    152155
     156  @VariableSetter
     157  @PathSetter
    153158  public void setIcon(String icon)
    154159  {
     
    161166  }
    162167
     168  @VariableSetter
    163169  public void setOnClick(String onClick)
    164170  {
     
    201207  }
    202208
    203  
    204209  @Override
    205210  public String toString()
     
    216221    sb.append("; visible=" + visible);
    217222    sb.append("; enabled=" + enabled);
     223    if (getScripts() != null) sb.append("; scripts=" + getScripts());
     224    if (getStylesheets() != null) sb.append("; stylesheets=" + getStylesheets());
    218225    return sb.toString();
    219226  }
  • branches/extensions/src/clients/web/net/sf/basedb/clients/web/servlet/ExtensionsServlet.java

    r4168 r4170  
    3939import net.sf.basedb.util.RegexpFileFilter;
    4040import net.sf.basedb.util.extensions.Registry;
    41 import net.sf.basedb.util.extensions.XmlLoader;
     41import net.sf.basedb.util.extensions.xml.PathConverter;
     42import net.sf.basedb.util.extensions.xml.VariableConverter;
     43import net.sf.basedb.util.extensions.xml.XmlLoader;
    4244
    4345/**
     
    9395  {
    9496    XmlLoader loader = new XmlLoader();
     97    VariableConverter variableConverter = new VariableConverter();
     98    variableConverter.setVariable("ROOT", "/extensions");
     99    variableConverter.setVariable("HOME", extensionsPath);
     100    loader.addValueConverter(variableConverter);
     101   
     102    PathConverter pathConverter = new PathConverter("/extensions", extensionsPath);
     103    loader.addValueConverter(pathConverter);
     104   
    95105    // Load core extension points
    96106    URL coreExtensionPoints =
     
    112122    {
    113123      if (xmlFile.getName().equals("settings.xml")) continue;
     124      pathConverter.setHome("/extensions/extensions/" + xmlFile.getName());
    114125      try
    115126      {
  • branches/extensions/src/core/net/sf/basedb/util/extensions/AboutBean.java

    r4163 r4170  
    2828/**
    2929  An implementation of the {@link About} interface, which
    30   complements all methods with setter methods.
     30  complements all getter methods with setter methods.
    3131 
    3232  @author nicklas
  • branches/extensions/src/core/net/sf/basedb/util/extensions/ActionFactory.java

    r4163 r4170  
    2424package net.sf.basedb.util.extensions;
    2525
     26import net.sf.basedb.util.extensions.xml.XmlLoader;
     27
    2628/**
    27   An action factory is an object which knows how to create actions.
    28   An action factory is defined by an {@link Extension}. The implementation
    29   must be thread safe since only a single instance of the factory exists
    30   to serve all requests.
     29  An action factory is an object which knows how to create
     30  {@link Action}:s. Action factories are part of an extension
     31  (see {@link Extension#getActionFactory()}).
     32 
     33  <p>
     34  Action factory implementations must be thread-safe, since a
     35  single instance of the factory may have to serve multiple requests
     36  at the same time.
     37 
     38  <p>
     39  Note 1! A single factory instance may be used by more than one
     40  extension. This is at the control of the client application.
     41 
     42  <p>
     43  Note 2! The BASE web-client uses the {@link XmlLoader} to
     44  define extensions. The <code>XmlLoader</code> always create a
     45  separate factory instance for each extension. The <code>XmlLoader</code>
     46  also requires that the factories has a public no-argument constructor.
    3147
    3248  @author nicklas
     
    3854
    3955  /**
    40     This method is called once for each request and should be used
    41     by the factory to decide if the extension should be enabled or not and
    42     to initialise the context with, for example, resources that the
    43     actions may need. In the case of the web client, this may for
    44     example, be any scripts and/or style sheet files that is needed
    45     by the extension.
     56    This method is called once for each request/use of an
     57    extension and have two purposes:
    4658   
    47     @param context The current context
    48     @return TRUE if the extension should be enabled, FALSE if the extension should
    49       be disabled
     59    <ul>
     60    <li>The factory should decide if the extension should be enabled or
     61      not. For example, the factory may check the permissions of the
     62      logged in user and determine that they are inadequate. The boolean
     63      return value determines if the extension is enabled or disabled.
     64     
     65    <li>Initialise the context with resources that the actions may need.
     66      With the BASE web-client this means that it is possible to
     67      add scripts or stylesheets that is needed by the extension.
     68      See {@link JspContext}.
     69    </ul>
     70   
     71    @param context The current context to prepare
     72    @param extension The extension to use
     73    @return TRUE if the extension should be enabled,
     74      FALSE if the extension should be disabled
    5075  */
    51   public boolean prepareContext(Context context);
     76  public boolean prepareContext(Context context, Extension<? super A> extension);
    5277 
    5378  /**
     
    6186   
    6287    @param context The current context
     88    @param extension The extension to create actions for
    6389    @return An array of actions that should be added to the extension point.
    6490      Returns null or an empty array if there are no actions in the current context.
    6591  */
    66   public A[] getActions(Context context);
     92  public A[] getActions(Context context, Extension<? super A> extension);
    6793 
    6894}
  • branches/extensions/src/core/net/sf/basedb/util/extensions/ActionIterator.java

    r4168 r4170  
     1/**
     2  $Id:  $
     3
     4  Copyright (C) Authors contributing to this file.
     5
     6  This file is part of BASE - BioArray Software Environment.
     7  Available at http://base.thep.lu.se/
     8
     9  BASE is free software; you can redistribute it and/or
     10  modify it under the terms of the GNU General Public License
     11  as published by the Free Software Foundation; either version 2
     12  of the License, or (at your option) any later version.
     13
     14  BASE is distributed in the hope that it will be useful,
     15  but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17  GNU General Public License for more details.
     18
     19  You should have received a copy of the GNU General Public License
     20  along with this program; if not, write to the Free Software
     21  Foundation, Inc., 59 Temple Place - Suite 330,
     22  Boston, MA  02111-1307, USA.
     23*/
    124package net.sf.basedb.util.extensions;
    225
     
    1033
    1134/**
    12   Iterator for iteratoring over all actions created by
    13   extensions by {@link ExtensionsInvoker#i
    14   over all actions.
     35  Iterator for iterating over all actions that <b>will be
     36  created</b> by extensions after a call to {@link
     37  Registry#useExtensions(Context, ExtensionsFilter, String...)}.
     38  That method returns an {@link ExtensionsInvoker} object which
     39  in it's turn uses this iterator.
     40 
     41  <p>
     42  Note the phrase "will be created" above. The actions
     43  are created on demand as this iterator moves on the
     44  next item. This happens in the {@link #hasNext()} method, which,
     45  for each extension, calls {@link Extension#getActionFactory()}
     46  and then {@link ActionFactory#getActions(Context, Extension)}.
     47 
     48  <p>
     49  The actions returned from the extensions are piled up in an
     50  internal buffer that the {@link #next()} method pick items from.
     51  When the buffer is empty the iterator moves on to the actions from
     52  next extension until there are no more extensions and the action
     53  buffer is empty.
     54
     55  @author nicklas
     56  @version 2.7
     57  @base.modified $Date$
    1558*/
    1659public class ActionIterator<A extends Action>
     
    3376 
    3477  // The renderer for the current action
    35   private Renderer<A> currentRenderer;
     78  private Renderer<? super A> currentRenderer;
    3679 
    3780  // Cache for created renderers
    38   private Map<Object, Renderer<A>> rendererCache;
     81  private Map<Object, Renderer<? super A>> rendererCache;
    3982 
    4083  // Boolean flags to indicate if we have a next element and if
     
    4992    this.iterator = iterator;
    5093    this.checkNext = true;
    51     this.rendererCache = new IdentityHashMap<Object, Renderer<A>>();
     94    this.rendererCache = new IdentityHashMap<Object, Renderer<? super A>>();
    5295  }
    5396 
     
    78121          currentExtension = iterator.next();
    79122          // Get the actions for the extension
    80           actions = currentExtension.getActionFactory().getActions(context);
     123          actions = currentExtension.getActionFactory().getActions(context, currentExtension);
    81124          if (actions != null && actions.length > 0)
    82125          {
     
    110153    Get the extension that created the current action.
    111154  */
    112   public Extension<A> getExtension()
     155  public Extension<? extends A> getExtension()
    113156  {
    114157    return currentExtension;
     
    118161    Gets the renderer for the current action.
    119162  */
    120   Renderer<A> getRenderer()
     163  Renderer<? super A> getRenderer()
    121164  {
    122165    if (currentRenderer == null)
    123166    {
    124       RegisteredExtensionPoint<A> ep = currentExtension.getExtensionPoint();
    125       RendererFactory<A> rf = currentExtension.getRendererFactory();
     167      RegisteredExtensionPoint<? super A> ep = currentExtension.getExtensionPoint();
     168      RendererFactory<? super A> rf = currentExtension.getRendererFactory();
    126169      if (ep.allowRendererOverride() && rf != null)
    127170      {
    128171        // Use the extension's renderer factory if it is
    129172        // allowed and if one is provided
    130         currentRenderer = rf.getRenderer(context);
     173        currentRenderer = rf.getRenderer(context, currentExtension);
    131174      }
    132175      else
     
    137180        if (currentRenderer == null)
    138181        {
    139           currentRenderer = ep.getRendererFactory().getRenderer(context);
     182          currentRenderer = ep.getRendererFactory().getRenderer(context, currentExtension);
    140183          rendererCache.put(ep, currentRenderer);
    141184        }
  • branches/extensions/src/core/net/sf/basedb/util/extensions/Context.java

    r4168 r4170  
    4141  The current context is passed to an {@link ActionFactory} twice, first
    4242  to check if an extension should be enabled or not
    43   with the {@link ActionFactory#prepareContext(Context)}
     43  with the {@link ActionFactory#prepareContext(Context, Extension)}
    4444  method. This method also allows an extension to write
    4545  back information to the context. This may, for example,
     
    5050  <p>
    5151  The second time is when calling the {@link
    52   ActionFactory#getActions(Context)} method. This method can
     52  ActionFactory#getActions(Context, Extension)} method. This method can
    5353  be called multiple times. For example, if an extension point
    5454  is in a list context, the <code>getActions</code> method may
     
    7373 
    7474  /**
    75     Create a new context.
     75    Create a new context with a session. The session is final and can't
     76    be replaced.
    7677    @param sc The current session
    7778  */
     
    8283 
    8384  /**
    84     Create a new context.
     85    Create a new context with a session and item. The session is final and
     86    can't be replaced. Use {@link #setCurrentItem(Object)} to change the item.
     87   
    8588    @param sc The current session
    8689    @param item The current item
  • branches/extensions/src/core/net/sf/basedb/util/extensions/DefaultFilter.java

    r4168 r4170  
    4646    @see Extension#getIndex()
    4747   */
    48   public static final Comparator<Extension> INDEX_COMPARATOR =
    49     new Comparator<Extension>()
     48  public static final Comparator<Extension<?>> INDEX_COMPARATOR =
     49    new Comparator<Extension<?>>()
    5050    {
    5151      @Override
    52       public int compare(Extension o1, Extension o2)
     52      public int compare(Extension<?> o1, Extension<?> o2)
    5353      {
    5454        return Float.compare(o1.getIndex(), o2.getIndex());
     
    6767  */
    6868  @Override
    69   public boolean isEnabled(ExtensionPoint extensionPoint)
     69  public boolean isEnabled(ExtensionPoint<?> extensionPoint)
    7070  {
    7171    return true;
     
    7373
    7474  @Override
    75   public boolean isEnabled(Extension extension)
     75  public boolean isEnabled(Extension<?> extension)
    7676  {
    7777    return true;
     
    7979
    8080  @Override
    81   public void sort(List<? extends Extension> extensions)
     81  public void sort(List<? extends Extension<?>> extensions)
    8282  {
    8383    Collections.sort(extensions, INDEX_COMPARATOR);
  • branches/extensions/src/core/net/sf/basedb/util/extensions/Extension.java

    r4163 r4170  
    2525
    2626import net.sf.basedb.core.plugin.About;
     27import net.sf.basedb.util.extensions.xml.XmlLoader;
    2728
    2829/**
     
    4243  Extensions can, for example, be created:
    4344  <ul>
    44   <li>Programmatically, using {@link ExtensionBean} object
     45  <li>Programmatically, using an {@link ExtensionBean} object.
    4546  <li>Loaded from extension definition XML files using a {@link XmlLoader}.
    46   <li>Or, in any other way by providing a custom implementation of this interface
     47  <li>Or, in any other way by providing a custom implementation of this interface.
    4748  </ul>
    4849 
     
    7374 
    7475  /**
    75     The extensions in an extension point are ordered by their
     76    The extensions in an extension point are by default ordered by their
    7677    index value. Extensions with a low value are ordered before
    77     extensions with a high value.
     78    extensions with a high value. The ordering may be overridden by providing
     79    an external {@link ExtensionsFilter} implementation.
    7880    @return The index value
    7981  */
     
    9294    objects of the same class or a subclass as the {@link ExtensionPoint#getActionClass()}
    9395    method of the extended extension point returns.
    94     @return An action factory
     96    @return An action factory (required)
    9597  */
    96   public ActionFactory<A> getActionFactory();
     98  public ActionFactory<? extends A> getActionFactory();
    9799 
    98100  /**
     
    103105    @see ExtensionPoint#allowRendererOverride()
    104106  */
    105   public RendererFactory<A> getRendererFactory();
     107  public RendererFactory<? super A> getRendererFactory();
    106108 
    107109}
  • branches/extensions/src/core/net/sf/basedb/util/extensions/ExtensionBean.java

    r4163 r4170  
    4141  private String id;
    4242  private String extensionPoint;
    43   private ActionFactory<A> actionFactory;
    44   private RendererFactory<A> rendererFactory;
     43  private ActionFactory<? extends A> actionFactory;
     44  private RendererFactory<? super A> rendererFactory;
    4545  private About about;
    4646  private float index;
     
    6363    float position,
    6464    About about,
    65     ActionFactory<A> actionFactory,
    66     RendererFactory<A> rendererFactory)
     65    ActionFactory<? extends A> actionFactory,
     66    RendererFactory<? super A> rendererFactory)
    6767  {
    6868    this.id = id;
     
    105105 
    106106  @Override
    107   public ActionFactory<A> getActionFactory()
     107  public ActionFactory<? extends A> getActionFactory()
    108108  {
    109109    return actionFactory;
    110110  }
    111   public void setActionFactory(ActionFactory<A> actionFactory)
     111  public void setActionFactory(ActionFactory<? extends A> actionFactory)
    112112  {
    113113    this.actionFactory = actionFactory;
     
    115115 
    116116  @Override
    117   public RendererFactory<A> getRendererFactory()
     117  public RendererFactory<? super A> getRendererFactory()
    118118  {
    119119    return rendererFactory;
    120120  }
    121   public void setRendererFactory(RendererFactory<A> rendererFactory)
     121  public void setRendererFactory(RendererFactory<? super A> rendererFactory)
    122122  {
    123123    this.rendererFactory = rendererFactory;
  • branches/extensions/src/core/net/sf/basedb/util/extensions/ExtensionPoint.java

    r4168 r4170  
    2424package net.sf.basedb.util.extensions;
    2525
     26import net.sf.basedb.util.extensions.xml.XmlLoader;
     27
    2628
    2729/**
     
    4850  an extension point are registered in the same registry with
    4951  {@link Registry#registerExtension(Extension)}. To load and use all
    50   registered extensions use {@link Registry#getExtensions(String, Context)}.
     52  registered extensions use
     53  {@link Registry#useExtensions(Context, ExtensionsFilter, String...)}.
    5154
    5255  @author nicklas
     
    8891    extension point must provide an {@link ActionFactory} that is capable
    8992    of creating instances of the specified class.
     93   
    9094    @return The class of the actions that can be used on this extension point
     95      (required)
    9196  */
    9297  public Class<A> getActionClass();
     
    100105    @return A renderer factory or null
    101106  */
    102   public RendererFactory<A> getRendererFactory();
     107  public RendererFactory<? super A> getRendererFactory();
    103108 
    104109  /**
  • branches/extensions/src/core/net/sf/basedb/util/extensions/ExtensionPointBean.java

    r4168 r4170  
    4141  private String description;
    4242  private Class<A> actionClass;
    43   private RendererFactory<A> rendererFactory;
     43  private RendererFactory<? super A> rendererFactory;
    4444  private boolean allowRendererOverride;
    4545 
     
    114114
    115115  @Override
    116   public RendererFactory<A> getRendererFactory()
     116  public RendererFactory<? super A> getRendererFactory()
    117117  {
    118118    return rendererFactory;
    119119  }
    120   public void setRendererFactory(RendererFactory<A> rendererFactory)
     120  public void setRendererFactory(RendererFactory<? super A> rendererFactory)
    121121  {
    122122    this.rendererFactory = rendererFactory;
  • branches/extensions/src/core/net/sf/basedb/util/extensions/ExtensionsFilter.java

    r4168 r4170  
    4545      it is disabled
    4646  */
    47   public boolean isEnabled(ExtensionPoint extensionPoint);
     47  public boolean isEnabled(ExtensionPoint<?> extensionPoint);
    4848 
    4949  /**
     
    5353      it is disabled
    5454  */
    55   public boolean isEnabled(Extension extension);
     55  public boolean isEnabled(Extension<?> extension);
    5656 
    5757  /**
     
    5959    @param extensions The list of extensions to sort
    6060  */
    61   public void sort(List<? extends Extension> extensions);
     61  public void sort(List<? extends Extension<?>> extensions);
    6262 
    6363}
  • branches/extensions/src/core/net/sf/basedb/util/extensions/ExtensionsInvoker.java

    r4168 r4170  
    3232/**
    3333  Object of this class handles a single invokation of the extensions
    34   for one or several extension points. Call {@link Registry#useExtensions(Context, String...)}
     34  for one or several extension points. Call
     35  {@link Registry#useExtensions(Context, ExtensionsFilter, String...)}
    3536  to create an invoker object. With this object you can
    3637  {@link #iterate()} over all actions created by the extensions, or
     
    4344  Since the registration of rendering factories are optional, the
    4445  <code>renderDefault()</code> method only works with extensions that have
    45   done so.
     46  done so. If an action can't be associated with a renderer, an exception
     47  is thrown.
    4648
    4749  <p>
    4850  If there is no default rendering factory or if you want to override the default
    49   renderer, you can use the {@link #render(Renderer)} method. This will always
    50   work, even if an extension point has specified that the rendering factory
    51   can't be overridden (this only applies to extensions).
     51  renderer, you can use the {@link #render(Renderer)} method. This will force
     52  the use of the specified renderer for all extensions, ignoring all renderers
     53  set by extension points or extensions.
    5254
    5355  @author nicklas
     
    6264  private RendererFactory<A> currentRenderFactory;
    6365 
     66  /**
     67    Creates a new invoker object.
     68  */
    6469  ExtensionsInvoker(Collection<RegisteredExtension<A>> extensions, Context context)
    6570  {
     
    9297    {
    9398      A action = it.next();
    94       Renderer<A> renderer = it.getRenderer();
     99      Renderer<? super A> renderer = it.getRenderer();
    95100      if (renderer == null)
    96101      {
  • branches/extensions/src/core/net/sf/basedb/util/extensions/Registry.java

    r4168 r4170  
    3737
    3838import net.sf.basedb.core.plugin.About;
     39import net.sf.basedb.util.extensions.xml.XmlLoader;
    3940
    4041/**
     
    4344  be created programmatically using the {@link ExtensionPointBean}
    4445  and {@link ExtensionBean} implementations, or loaded from
    45   an extensions definition XML file with the {@link XmlLoader}
     46  an XML extension definition file with the {@link XmlLoader}
    4647  class. Applications may provide other means of creating and
    4748  registering extensions and extensions points.
     
    314315    depending on the application, some information about what it about to
    315316    happen. For example, that we are going to display a list of samples.
     317   
    316318    <p>
    317319    Each extension that has been registered with the extension points will
    318320    get a chance to check the context object and to further initialise it
    319321    with extension-dependent information. This is a task for the
    320     {@link ActionFactory#prepareContext(Context)} method. If this method returns
    321     false the extension will not be included in the invoker. Extensions
     322    {@link ActionFactory#prepareContext(Context, Extension)} method. If this method
     323    returns false the extension will not be included in the invoker. Extensions
    322324    may for example use this to check if the logged in user has enough permissions
    323325    to be able to use the extension.
     326   
     327    <p>
     328    If the extension points and/or extensions also provide renderer factories,
     329    the {@link RendererFactory#prepareContext(Context, Extension)} is called.
     330    For renderer factories that are attached to extension points the method
     331    is only called if at least one enabled extension exists. If the method is
     332    called, it is only called once with a null argument for the extension.
     333    Renderer factories that are attached to extensions are called once for each
     334    extension, but only if the extension point allows it.
     335    See {@link ExtensionPoint#allowRendererOverride()}.
    324336   
    325337    <p>
     
    329341   
    330342    @param context Information about the current context
    331     @param filter A filter object that can be used to filter out
     343    @param filter A filter object that can be used to filter out
    332344      disabled extensions and sort the extensions in a particular order.
    333345      If this value is null, no extensions are filtered out and they
    334346      are sorted according to their {@link Extension#getIndex()} value
    335     @param extensionPoints An array of extension point ID:s to get
     347    @param extensionPoints An array of extension point ID:s to get
    336348      extension for. In most cases the extension points should
    337349      require the same action class but this is not required
     
    340352  */
    341353  @SuppressWarnings("unchecked")
    342   public ExtensionsInvoker<?> useExtensions(Context context, ExtensionsFilter filter, String... extensionPoints)
     354  public ExtensionsInvoker<?> useExtensions(Context context, ExtensionsFilter filter,
     355    String... extensionPoints)
    343356  {
    344357    if (filter == null) filter = DEFAULT_FILTER;
     
    350363      if (rep != null && filter.isEnabled(rep))
    351364      {
     365        boolean allowRenderOverride = rep.allowRendererOverride();
     366        int enabledExtensions = 0;
    352367        for (RegisteredExtension<Action> ext : rep.getExtensions())
    353368        {
    354           if (filter.isEnabled(ext) && ext.getActionFactory().prepareContext(context))
     369          if (filter.isEnabled(ext) && ext.getActionFactory().prepareContext(context, ext))
    355370          {
    356371            use.add(ext);
     372            enabledExtensions++;
     373            if (allowRenderOverride)
     374            {
     375              RendererFactory<? super Action> factory = ext.getRendererFactory();
     376              if (factory != null) factory.prepareContext(context, ext);
     377            }
    357378          }
     379        }
     380        RendererFactory<? super Action> factory = rep.getRendererFactory();
     381        if (enabledExtensions > 0 && factory != null)
     382        {
     383          factory.prepareContext(context, null);
    358384        }
    359385      }
     
    380406    private String name;
    381407    private String description;
    382     private RendererFactory<A> rendererFactory;
     408    private RendererFactory<? super A> rendererFactory;
    383409    private boolean allowRendererOverride;
    384410   
     
    429455   
    430456    @Override
    431     public RendererFactory<A> getRendererFactory()
     457    public RendererFactory<? super A> getRendererFactory()
    432458    {
    433459      return rendererFactory;
     
    490516  {
    491517
    492     private final RegisteredExtensionPoint<A> rep;
     518    private final RegisteredExtensionPoint<? super A> rep;
    493519    private final String id;
    494520    private final String extensionPoint;
    495521    private RegisteredAbout about;
    496522    private float index;
    497     private ActionFactory<A> actionFactory;
    498     private RendererFactory<A> rendererFactory;
     523    private ActionFactory<? extends A> actionFactory;
     524    private RendererFactory<? super A> rendererFactory;
    499525
    500526    /**
     
    502528      information from the parameter.
    503529    */
    504     RegisteredExtension(Extension<A> extension, RegisteredExtensionPoint<A> rep)
     530    RegisteredExtension(Extension<A> extension, RegisteredExtensionPoint<? super A> rep)
    505531    {
    506532      this.rep = rep;
    507533      this.id = extension.getId();
    508534      this.extensionPoint = extension.getExtends();
    509       this.index = extension.getIndex();
    510       this.about = extension.getAbout() == null ?
    511         null : new RegisteredAbout(extension.getAbout());
    512       this.actionFactory = extension.getActionFactory();
    513       this.rendererFactory = extension.getRendererFactory();
     535      update(extension);
    514536    }
    515537
     
    537559
    538560    @Override
    539     public ActionFactory<A> getActionFactory()
     561    public ActionFactory<? extends A> getActionFactory()
    540562    {
    541563      return actionFactory;
     
    543565   
    544566    @Override
    545     public RendererFactory<A> getRendererFactory()
     567    public RendererFactory<? super A> getRendererFactory()
    546568    {
    547569      return rendererFactory;
     
    589611      Get the extension point this extension is registered with.
    590612    */
    591     RegisteredExtensionPoint<A> getExtensionPoint()
     613    RegisteredExtensionPoint<? super A> getExtensionPoint()
    592614    {
    593615      return rep;
  • branches/extensions/src/core/net/sf/basedb/util/extensions/RendererFactory.java

    r4163 r4170  
    2424package net.sf.basedb.util.extensions;
    2525
     26import net.sf.basedb.util.extensions.xml.XmlLoader;
     27
    2628/**
    2729  A renderer factory is a factory that can create {@link Renderer}
     
    3032  extension point an extension may provide it's own factory
    3133  implementation ({@link Extension#getRendererFactory()}.
    32   The factory implementation must be thread safe since only a
    33   single instance of the factory exists to serve all requests.
     34
     35  <p>
     36  Renderer factory implementations must be thread-safe, since a
     37  single instance of the factory may have to serve multiple requests
     38  at the same time.
     39 
     40  <p>
     41  Note 1! A single factory instance may be used by more than one
     42  extension/extension point. This is at the control of the client application.
     43 
     44  <p>
     45  Note 2! The BASE web-client uses the {@link XmlLoader} to
     46  define extensions. The <code>XmlLoader</code> always create a
     47  separate factory instance for each extension/extension point.
     48  The <code>XmlLoader</code> also requires that the factories has
     49  a public no-argument constructor.
    3450
    3551  @author nicklas
     
    4157
    4258  /**
     59    This method is called once for each request/use of an
     60    extension and should be used by a factory to initialise the context
     61    with resources that the actions may need. With the BASE web-client
     62    this means that it is possible to add scripts or stylesheets that
     63    is needed by the extension. See {@link JspContext}.
     64   
     65    <p>
     66    Note! This method has no return as opposed to
     67    {@link ActionFactory#prepareContext(Context, Extension). The
     68    simple reason is that once we get to the point of rendering it
     69    is already known that the extension is enabled.
     70   
     71    @param context The current context to prepare
     72    @param extension The extension to use, or null if this
     73      factory is used from an extension point
     74  */
     75  public void prepareContext(Context context, Extension<? extends A> extension);
     76 
     77  /**
    4378    Get a renderer for a given context. This method may
    4479    create a new instance or use an existing instance. If
     
    4782   
    4883    @param context The current context
     84    @param extension The extension to create a renderer for
    4985    @return A renderer instance
    5086  */
    51   public Renderer<A> getRenderer(Context context);
     87  public Renderer<? super A> getRenderer(Context context, Extension<? extends A> extension);
    5288 
    5389}
  • branches/extensions/src/core/net/sf/basedb/util/extensions/xml/XmlLoader.java

    r4168 r4170  
    2222  Boston, MA  02111-1307, USA.
    2323*/
    24 package net.sf.basedb.util.extensions;
     24package net.sf.basedb.util.extensions.xml;
    2525
    2626import java.io.IOException;
     
    2828import java.lang.reflect.InvocationTargetException;
    2929import java.lang.reflect.Method;
     30import java.util.ArrayList;
    3031import java.util.LinkedList;
    3132import java.util.List;
     
    3940import net.sf.basedb.util.Values;
    4041import net.sf.basedb.util.XMLUtil;
     42import net.sf.basedb.util.extensions.AboutBean;
     43import net.sf.basedb.util.extensions.Action;
     44import net.sf.basedb.util.extensions.ActionFactory;
     45import net.sf.basedb.util.extensions.Extension;
     46import net.sf.basedb.util.extensions.ExtensionBean;
     47import net.sf.basedb.util.extensions.ExtensionPoint;
     48import net.sf.basedb.util.extensions.ExtensionPointBean;
     49import net.sf.basedb.util.extensions.Registry;
     50import net.sf.basedb.util.extensions.RendererFactory;
    4151
    4252/**
     
    6171  use reflection to find a setter method for each sub-tag inside
    6272  the <code>&lt;parameters&gt;</code> tag. Here is an example:
     73
    6374  <pre class="code">
    6475// XML contents
     
    7384</pre>
    7485
    75   The setter methods must be public and accept a single String parameter.
     86  The setter methods must be public and accept a single <code>String</code> parameter.
    7687  Tags that doesn't have a corresponding setter method are simply ignored.
    7788  The XML file format allows any tags as parameters, but only the first level
    7889  will be parsed.
     90 
     91  <p>
     92  The parameter values may be subject to conversion by one or more
     93  {@link ValueConverter}:s. Converters are added to the loader
     94  by {@link #addValueConverter(ValueConverter)} and are usually
     95  implemented to react on method annotations. For example, the web
     96  client uses this for path and variable substitution.
    7997
    8098  @author nicklas
     
    102120  private List<Extension<Action>> extensions;
    103121 
     122  // List of registered converters
     123  private List<ValueConverter> converters;
     124 
    104125  /**
    105126    Create a new XML loader instance.
     
    109130    extensionPoints = new LinkedList<ExtensionPoint<Action>>();
    110131    extensions = new LinkedList<Extension<Action>>();
     132  }
     133 
     134 
     135  public void addValueConverter(ValueConverter converter)
     136  {
     137    if (converters == null) converters = new ArrayList<ValueConverter>();
     138    converters.add(converter);
    111139  }
    112140 
     
    390418        {
    391419          Method setter = beanClass.getMethod(methodName, String.class);
     420         
     421          // Convert input values
     422          if (converters != null)
     423          {
     424            for (ValueConverter converter : converters)
     425            {
     426              value = converter.convert(value, setter);
     427            }
     428          }
    392429          setter.invoke(bean, value);
    393430        }
     
    402439  }
    403440 
     441
    404442}
  • branches/extensions/src/test/TestExtensions.java

    r4169 r4170  
    2828import net.sf.basedb.util.extensions.Action;
    2929import net.sf.basedb.util.extensions.ActionFactory;
    30 import net.sf.basedb.util.extensions.XmlLoader;
    3130import net.sf.basedb.util.extensions.Context;
    3231import net.sf.basedb.util.extensions.Extension;
     
    3635import net.sf.basedb.util.extensions.Renderer;
    3736import net.sf.basedb.util.extensions.RendererFactory;
     37import net.sf.basedb.util.extensions.xml.VariableConverter;
     38import net.sf.basedb.util.extensions.xml.VariableSetter;
     39import net.sf.basedb.util.extensions.xml.XmlLoader;
    3840
    3941
     
    9799     
    98100      XmlLoader loader = new XmlLoader();
     101      VariableConverter converter = new VariableConverter();
     102      converter.setVariable("HOME", "~");
     103      loader.addValueConverter(converter);
    99104      loader.loadXmlFile(in, xmlFile, null);
    100105     
     
    127132     
    128133      XmlLoader loader = new XmlLoader();
     134      VariableConverter converter = new VariableConverter();
     135      converter.setVariable("HOME", "~");
     136      loader.addValueConverter(converter);
    129137      loader.loadXmlFile(in, xmlFile, null);
    130138     
     
    177185  {
    178186    private final String title;
    179     public ActionButton(String title)
     187    private final String icon;
     188   
     189    public ActionButton(String title, String icon)
    180190    {
    181191      this.title = title;
     192      this.icon = icon;
    182193    }
    183194   
     
    185196    {
    186197      return title;
     198    }
     199   
     200    public String getIcon()
     201    {
     202      return icon;
    187203    }
    188204  }
     
    193209
    194210    private String title;
     211    private String icon;
    195212    private ActionButton button;
    196213   
     
    199216   
    200217    @Override
    201     public ActionButton[] getActions(Context context)
     218    public ActionButton[] getActions(Context context, Extension<? super ActionButton> extension)
    202219    {
    203220      if (button == null)
    204221      {
    205         button = new ActionButton(title);
     222        button = new ActionButton(title, icon);
    206223      }
    207224      return new ActionButton[] { button };
     
    209226
    210227    @Override
    211     public boolean prepareContext(Context context)
     228    public boolean prepareContext(Context context, Extension<? super ActionButton> extension)
    212229    {
    213230      return true;
     
    217234    {
    218235      this.title = title;
     236    }
     237   
     238    @VariableSetter
     239    public void setIcon(String icon)
     240    {
     241      this.icon = icon;
    219242    }
    220243   
     
    236259
    237260    @Override
    238     public Renderer<ActionButton> getRenderer(Context context)
     261    public void prepareContext(Context context, Extension<? extends ActionButton> extension)
     262    {
     263    }
     264   
     265    @Override
     266    public Renderer<ActionButton> getRenderer(Context context, Extension<? extends ActionButton> extension)
    239267    {
    240268      return this;
     
    247275      {
    248276        System.out.println("<div class=\"actionbutton\" style=\"color: " +
    249             color + ";\">" + action.getTitle() + "</div>");
     277            color + ";\"><img src=\"" + action.getIcon() + "\">" +
     278            action.getTitle() + "</div>");
    250279      }
    251280    }
  • branches/extensions/src/test/data/test.extensions.xml

    r4163 r4170  
    4848      <parameters>
    4949        <title>Launch MeV</title>
     50        <icon>$HOME$/images/mev.png</icon>
    5051      </parameters>
    5152    </action-factory>
     
    6162      <parameters>
    6263        <title>Mage export</title>
     64        <icon>/images/mev.png</icon>
    6365      </parameters>
    6466    </action-factory>
  • branches/extensions/www/WEB-INF/extensions/test-extensions.xml

    r4168 r4170  
    9898      <parameters>
    9999        <title>Launch MeV</title>
    100         <icon>../../../images/tm4.png</icon>
     100        <icon>/images/tm4.png</icon>
    101101        <onClick>net_sf_launchMeV</onClick>
    102102      </parameters>
     
    116116      <parameters>
    117117        <title>Create MAGE</title>
    118         <icon>../../../images/copy.gif</icon>
     118        <icon>/images/copy.gif</icon>
    119119        <onClick>alert('Are you sure?')</onClick>
    120120      </parameters>
  • branches/extensions/www/views/experiments/bioassaysets/analysis_tree.jsp

    r4168 r4170  
    288288    <%
    289289      jspContext.setOut(out);
    290       jspContext.writeScripts(rootPath);
     290      jspContext.writeScripts();
    291291    %>
    292292    <script language="JavaScript">
Note: See TracChangeset for help on using the changeset viewer.