001    package org.apache.myfaces.tobago.context;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one or more
005     * contributor license agreements.  See the NOTICE file distributed with
006     * this work for additional information regarding copyright ownership.
007     * The ASF licenses this file to You under the Apache License, Version 2.0
008     * (the "License"); you may not use this file except in compliance with
009     * the License.  You may obtain a copy of the License at
010     *
011     *      http://www.apache.org/licenses/LICENSE-2.0
012     *
013     * Unless required by applicable law or agreed to in writing, software
014     * distributed under the License is distributed on an "AS IS" BASIS,
015     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016     * See the License for the specific language governing permissions and
017     * limitations under the License.
018     */
019    
020    import org.apache.commons.collections.iterators.EmptyIterator;
021    import org.apache.commons.collections.iterators.ObjectArrayIterator;
022    import org.apache.commons.collections.iterators.SingletonIterator;
023    import org.apache.commons.lang.ArrayUtils;
024    import org.apache.commons.lang.StringUtils;
025    
026    import java.io.Serializable;
027    import java.util.ArrayList;
028    import java.util.Arrays;
029    import java.util.Iterator;
030    import java.util.List;
031    
032    /**
033     * A markup signs a component to be rendered different from the normal.
034     * E. g. <code>markup="emphasized"</code> might be rendered bold
035     * or a <code>markup="deleted"</code> might be rendered with line-through style.
036     * The concrete rendering depends from the theme.
037     * <p/>
038     * The markup can also hold more than one value, e. g. <code>markup="emphasized, deleted"</code>.
039     * <p/>
040     * The value of the markup is unmodifiable.
041     * <p/>
042     * A markup must be registered for a component, before it can be used.
043     * <p/>
044     * A markup should only contain ASCII characters and digits.
045     */
046    public final class Markup implements Serializable, Iterable<String> {
047    
048      public static final Markup NULL = new Markup((String) null);
049    
050      public static final Markup ASCENDING = valueOf("ascending");
051      public static final Markup CENTER = valueOf("center");
052      public static final Markup CLICKABLE = valueOf("clickable");
053      public static final Markup DESCENDING = valueOf("descending");
054      public static final Markup DELETED = valueOf("deleted");
055      public static final Markup DISABLED = valueOf("disabled");
056      public static final Markup ERROR = valueOf("error");
057      public static final Markup EVEN = valueOf("even");
058      public static final Markup FATAL = valueOf("fatal");
059      public static final Markup FIRST = valueOf("first");
060      public static final Markup FOLDER = valueOf("folder");
061      public static final Markup INFO = valueOf("info");
062      public static final Markup LEFT = valueOf("left");
063      public static final Markup MARKED = valueOf("marked");
064      public static final Markup MODAL = valueOf("modal");
065      public static final Markup NUMBER = valueOf("number");
066      public static final Markup ODD = valueOf("odd");
067      public static final Markup READONLY = valueOf("readonly");
068      public static final Markup REQUIRED = valueOf("required");
069      public static final Markup RESIZABLE = valueOf("resizable");
070      public static final Markup RIGHT = valueOf("right");
071      public static final Markup SELECTED = valueOf("selected");
072      public static final Markup SORTABLE = valueOf("sortable");
073      public static final Markup STRONG = valueOf("strong");
074      public static final Markup TOP = valueOf("top");
075      public static final Markup WARN = valueOf("warn");
076    
077      /* Just one of "values" and "value" must be null */
078    
079      private final String[] values;
080      private final String value;
081    
082      private Markup(String[] values) {
083        this.values = values;
084        this.value = null;
085      }
086    
087      private Markup(String value) {
088        this.values = null;
089        this.value = value;
090      }
091    
092      public static Markup valueOf(String[] values) {
093        if (values == null || values.length == 0) {
094          return null;
095        } else if (values.length == 1) {
096          return valueOf(values[0]);
097        } else {
098          Markup markup = new Markup((String[]) ArrayUtils.clone(values));
099          for (int i = 0; i < markup.values.length; i++) {
100            markup.values[i] = markup.values[i].trim();
101          }
102          return markup;
103        }
104      }
105    
106      public static Markup valueOf(String value) {
107        if (StringUtils.isEmpty(value)) {
108          return null;
109        }
110        if (value.contains(",")) {
111          String[] strings = StringUtils.split(value, ", \t\n");
112          return new Markup(strings);
113        } else {
114          return new Markup(value.trim());
115        }
116      }
117    
118      public static Markup valueOf(Object value) {
119        if (value == null) {
120          return null;
121        }
122        if (value instanceof Markup) {
123          return (Markup) value;
124        }
125        if (value instanceof String) {
126          return valueOf((String) value);
127        }
128        if (value instanceof String[]) {
129          return valueOf((String[]) value);
130        }
131        if (value instanceof Iterable) {
132          List<String> list = new ArrayList<String>();
133          for (Object object : (Iterable) value) {
134            list.add(object.toString());
135          }
136          return valueOf(list.toArray(new String[list.size()]));
137        }
138        return valueOf(value.toString());
139      }
140    
141      @Override
142      public boolean equals(Object o) {
143        if (this == o) {
144          return true;
145        }
146        if (o == null || getClass() != o.getClass()) {
147          return false;
148        }
149    
150        Markup markup = (Markup) o;
151    
152        if (value != null ? !value.equals(markup.value) : markup.value != null) {
153          return false;
154        }
155        if (!Arrays.equals(values, markup.values)) {
156          return false;
157        }
158    
159        return true;
160      }
161    
162      @Override
163      public int hashCode() {
164        int result = values != null ? Arrays.hashCode(values) : 0;
165        result = 31 * result + (value != null ? value.hashCode() : 0);
166        return result;
167      }
168    
169      public Iterator<String> iterator() {
170        if (value != null) {
171          return new SingletonIterator(value);
172        }
173        if (values != null) {
174          return new ObjectArrayIterator(values);
175        }
176        return EmptyIterator.INSTANCE;
177      }
178    
179      public Markup add(Markup markup) {
180        if (markup == null) {
181          return this;
182        }
183        if (markup == NULL) {
184          return this;
185        }
186        if (markup.value != null) {
187          return add(markup.value);
188        } else {
189          // this part is not optimized, but it will be used rarely, in the moment...
190          Markup result = this;
191          for (String summand : markup.values) {
192            result = result.add(summand);
193          }
194          return result;
195        }
196      }
197    
198      private Markup add(String summand) {
199        if (summand == null) {
200          return this;
201        }
202        if (values == null) {
203          if (value == null) {
204            return valueOf(summand);
205          } else {
206            if (summand.equals(value)) {
207              return this;
208            } else {
209              return valueOf(new String[]{value, summand});
210            }
211          }
212        } else {
213          if (ArrayUtils.contains(values, summand)) {
214            return this;
215          } else {
216            final String[] strings = new String[values.length + 1];
217            System.arraycopy(values, 0, strings, 0, values.length);
218            strings[values.length] = summand;
219            return valueOf(strings);
220          }
221        }
222      }
223    
224      public Markup remove(Markup markup) {
225        if (markup.value != null) {
226          return remove(markup.value);
227        } else {
228          // this part is not optimized, but it will be used rarely, in the moment...
229          Markup result = this;
230          for (String summand : markup.values) {
231            result = result.remove(summand);
232          }
233          return result;
234        }
235      }
236    
237      private Markup remove(String summand) {
238        if (summand == null) {
239          return this;
240        }
241        if (values == null) {
242          if (value == null) {
243            return this;
244          } else {
245            if (summand.equals(value)) {
246              return NULL;
247            } else {
248              return this;
249            }
250          }
251        } else {
252          if (ArrayUtils.contains(values, summand)) {
253            final String[] strings = new String[values.length - 1];
254            int found = 0;
255            for (int i = 0; i < strings.length; i++) {
256              if (values[i].equals(summand)) {
257                found++;
258              }
259              strings[i] = values[i + found];
260            }
261            return valueOf(strings);
262          } else {
263            return this;
264          }
265        }
266      }
267    
268      @Override
269      public String toString() {
270        if (value != null) {
271          return value;
272        }
273        if (values == null) {
274          return "null";
275        }
276        return Arrays.toString(values);
277      }
278    }