View Javadoc

1   package com.atlassian.plugin.web.descriptors;
2   
3   import java.util.Iterator;
4   import java.util.List;
5   
6   import com.atlassian.plugin.Plugin;
7   import com.atlassian.plugin.PluginParseException;
8   import com.atlassian.plugin.hostcontainer.HostContainer;
9   import com.atlassian.plugin.loaders.LoaderUtils;
10  import com.atlassian.plugin.util.Assertions;
11  import com.atlassian.plugin.web.Condition;
12  import com.atlassian.plugin.web.conditions.AbstractCompositeCondition;
13  import com.atlassian.plugin.web.conditions.AndCompositeCondition;
14  import com.atlassian.plugin.web.conditions.ConditionLoadingException;
15  import com.atlassian.plugin.web.conditions.InvertedCondition;
16  import com.atlassian.plugin.web.conditions.OrCompositeCondition;
17  
18  import org.dom4j.Element;
19  
20  /**
21   * This class contains the logic for constructing
22   * {@link com.atlassian.plugin.web.Condition} objects from a module descriptor's
23   * XML element. Its functionality is used by both
24   * {@link com.atlassian.plugin.web.descriptors.AbstractWebFragmentModuleDescriptor}
25   * and
26   * {@link com.atlassian.plugin.web.descriptors.DefaultWebPanelModuleDescriptor}.
27   *
28   * @since 2.5.0
29   */
30  public class ConditionElementParser
31  {
32      public static class CompositeType
33      {
34          public static final int OR = 0;
35          public static final int AND = 1;
36  
37          public static int parse(final String type) throws PluginParseException
38          {
39              if ("or".equalsIgnoreCase(type))
40              {
41                  return CompositeType.OR;
42              } else if ("and".equalsIgnoreCase(type))
43              {
44                  return CompositeType.AND;
45              } else
46              {
47                  throw new PluginParseException("Invalid condition type specified. type = " + type);
48              }
49          }
50      }
51  
52      /**
53       * Creates a condition.  Only temporary until conditions for web fragments can be converted to use {@link HostContainer}
54       */
55      public static interface ConditionFactory
56      {
57          Condition create(String className, Plugin plugin) throws ConditionLoadingException;
58      }
59  
60      private final ConditionFactory conditionFactory;
61  
62      public ConditionElementParser(ConditionFactory conditionFactory)
63      {
64          this.conditionFactory = conditionFactory;
65      }
66  
67      /**
68       * Create a condition for when this web fragment should be displayed.
69       *
70       * @param element Element of web-section, web-item, or web-panel.
71       * @param type    logical operator type
72       * @throws com.atlassian.plugin.PluginParseException
73       *
74       */
75      @SuppressWarnings("unchecked")
76      public Condition makeConditions(final Plugin plugin, final Element element, final int type) throws PluginParseException
77      {
78          Assertions.notNull("plugin == null", plugin);
79  
80          // make single conditions (all Anded together)
81          final List<Element> singleConditionElements = element.elements("condition");
82          Condition singleConditions = null;
83          if ((singleConditionElements != null) && !singleConditionElements.isEmpty())
84          {
85              singleConditions = makeConditions(plugin, singleConditionElements, type);
86          }
87  
88          // make composite conditions (logical operator can be specified by
89          // "type")
90          final List<Element> nestedConditionsElements = element.elements("conditions");
91          AbstractCompositeCondition nestedConditions = null;
92          if ((nestedConditionsElements != null) && !nestedConditionsElements.isEmpty())
93          {
94              nestedConditions = getCompositeCondition(type);
95              for (final Iterator<Element> iterator = nestedConditionsElements.iterator(); iterator.hasNext(); )
96              {
97                  final Element nestedElement = iterator.next();
98                  nestedConditions.addCondition(makeConditions(plugin, nestedElement, CompositeType
99                          .parse(nestedElement.attributeValue("type"))));
100             }
101         }
102 
103         if ((singleConditions != null) && (nestedConditions != null))
104         {
105             // Join together the single and composite conditions by this type
106             final AbstractCompositeCondition compositeCondition = getCompositeCondition(type);
107             compositeCondition.addCondition(singleConditions);
108             compositeCondition.addCondition(nestedConditions);
109             return compositeCondition;
110         } else if (singleConditions != null)
111         {
112             return singleConditions;
113         } else if (nestedConditions != null)
114         {
115             return nestedConditions;
116         }
117 
118         return null;
119     }
120 
121     public Condition makeConditions(final Plugin plugin, final List<Element> elements, final int type) throws PluginParseException
122     {
123         if (elements.isEmpty())
124         {
125             return null;
126         } else if (elements.size() == 1)
127         {
128             return makeCondition(plugin, elements.get(0));
129         } else
130         {
131             final AbstractCompositeCondition compositeCondition = getCompositeCondition(type);
132             for (final Iterator<Element> it = elements.iterator(); it.hasNext(); )
133             {
134                 final Element element = it.next();
135                 compositeCondition.addCondition(makeCondition(plugin, element));
136             }
137 
138             return compositeCondition;
139         }
140     }
141 
142     public Condition makeCondition(final Plugin plugin, final Element element) throws PluginParseException
143     {
144         try
145         {
146             String conditionClassName = element.attributeValue("class");
147             if (conditionClassName == null)
148             {
149                 throw new PluginParseException("Condition element must specify a class attribute");
150             }
151             final Condition condition = conditionFactory.create(conditionClassName, plugin);
152             condition.init(LoaderUtils.getParams(element));
153 
154             if ((element.attribute("invert") != null) && "true".equals(element.attributeValue("invert")))
155             {
156                 return new InvertedCondition(condition);
157             }
158 
159             return condition;
160         } catch (final ClassCastException e)
161         {
162             throw new PluginParseException("Configured condition class does not implement the Condition interface", e);
163         } catch (final ConditionLoadingException cle)
164         {
165             throw new PluginParseException("Unable to load the module's display conditions: " + cle.getMessage(), cle);
166         }
167     }
168 
169     private AbstractCompositeCondition getCompositeCondition(final int type) throws PluginParseException
170     {
171         switch (type)
172         {
173             case CompositeType.OR:
174                 return new OrCompositeCondition();
175             case CompositeType.AND:
176                 return new AndCompositeCondition();
177             default:
178                 throw new PluginParseException("Invalid condition type specified. type = " + type);
179         }
180     }
181 }