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 PURE = valueOf("pure");
068      public static final Markup READONLY = valueOf("readonly");
069      public static final Markup REQUIRED = valueOf("required");
070      public static final Markup RESIZABLE = valueOf("resizable");
071      public static final Markup RIGHT = valueOf("right");
072      public static final Markup SELECTED = valueOf("selected");
073      public static final Markup SORTABLE = valueOf("sortable");
074      public static final Markup STRONG = valueOf("strong");
075      public static final Markup TOP = valueOf("top");
076      public static final Markup WARN = valueOf("warn");
077    
078      /* Just one of "values" and "value" must be null */
079    
080      private final String[] values;
081      private final String value;
082    
083      private Markup(String[] values) {
084        this.values = values;
085        this.value = null;
086      }
087    
088      private Markup(String value) {
089        this.values = null;
090        this.value = value;
091      }
092    
093      public static Markup valueOf(String[] values) {
094        if (values == null || values.length == 0) {
095          return null;
096        } else if (values.length == 1) {
097          return valueOf(values[0]);
098        } else {
099          Markup markup = new Markup((String[]) ArrayUtils.clone(values));
100          for (int i = 0; i < markup.values.length; i++) {
101            markup.values[i] = markup.values[i].trim();
102          }
103          return markup;
104        }
105      }
106    
107      public static Markup valueOf(String value) {
108        if (StringUtils.isEmpty(value)) {
109          return null;
110        }
111        if (value.contains(",")) {
112          String[] strings = StringUtils.split(value, ", \t\n");
113          return new Markup(strings);
114        } else {
115          return new Markup(value.trim());
116        }
117      }
118    
119      public static Markup valueOf(Object value) {
120        if (value == null) {
121          return null;
122        }
123        if (value instanceof Markup) {
124          return (Markup) value;
125        }
126        if (value instanceof String) {
127          return valueOf((String) value);
128        }
129        if (value instanceof String[]) {
130          return valueOf((String[]) value);
131        }
132        if (value instanceof Iterable) {
133          List<String> list = new ArrayList<String>();
134          for (Object object : (Iterable) value) {
135            list.add(object.toString());
136          }
137          return valueOf(list.toArray(new String[list.size()]));
138        }
139        return valueOf(value.toString());
140      }
141    
142      @Override
143      public boolean equals(Object o) {
144        if (this == o) {
145          return true;
146        }
147        if (o == null || getClass() != o.getClass()) {
148          return false;
149        }
150    
151        Markup markup = (Markup) o;
152    
153        if (value != null ? !value.equals(markup.value) : markup.value != null) {
154          return false;
155        }
156        if (!Arrays.equals(values, markup.values)) {
157          return false;
158        }
159    
160        return true;
161      }
162    
163      @Override
164      public int hashCode() {
165        int result = values != null ? Arrays.hashCode(values) : 0;
166        result = 31 * result + (value != null ? value.hashCode() : 0);
167        return result;
168      }
169    
170      public Iterator<String> iterator() {
171        if (value != null) {
172          return new SingletonIterator(value);
173        }
174        if (values != null) {
175          return new ObjectArrayIterator(values);
176        }
177        return EmptyIterator.INSTANCE;
178      }
179    
180      public Markup add(Markup markup) {
181        if (markup == null) {
182          return this;
183        }
184        if (markup == NULL) {
185          return this;
186        }
187        if (markup.value != null) {
188          return add(markup.value);
189        } else {
190          // this part is not optimized, but it will be used rarely, in the moment...
191          Markup result = this;
192          for (String summand : markup.values) {
193            result = result.add(summand);
194          }
195          return result;
196        }
197      }
198    
199      private Markup add(String summand) {
200        if (summand == null) {
201          return this;
202        }
203        if (values == null) {
204          if (value == null) {
205            return valueOf(summand);
206          } else {
207            if (summand.equals(value)) {
208              return this;
209            } else {
210              return valueOf(new String[]{value, summand});
211            }
212          }
213        } else {
214          if (ArrayUtils.contains(values, summand)) {
215            return this;
216          } else {
217            final String[] strings = new String[values.length + 1];
218            System.arraycopy(values, 0, strings, 0, values.length);
219            strings[values.length] = summand;
220            return valueOf(strings);
221          }
222        }
223      }
224    
225      public Markup remove(Markup markup) {
226        if (markup.value != null) {
227          return remove(markup.value);
228        } else {
229          // this part is not optimized, but it will be used rarely, in the moment...
230          Markup result = this;
231          for (String summand : markup.values) {
232            result = result.remove(summand);
233          }
234          return result;
235        }
236      }
237    
238      private Markup remove(String summand) {
239        if (summand == null) {
240          return this;
241        }
242        if (values == null) {
243          if (value == null) {
244            return this;
245          } else {
246            if (summand.equals(value)) {
247              return NULL;
248            } else {
249              return this;
250            }
251          }
252        } else {
253          if (ArrayUtils.contains(values, summand)) {
254            final String[] strings = new String[values.length - 1];
255            int found = 0;
256            for (int i = 0; i < strings.length; i++) {
257              if (values[i].equals(summand)) {
258                found++;
259              }
260              strings[i] = values[i + found];
261            }
262            return valueOf(strings);
263          } else {
264            return this;
265          }
266        }
267      }
268    
269      public boolean contains(String markup) {
270        if (markup == null) {
271          return false;
272        }
273        if (this == NULL) {
274          return this == Markup.valueOf(markup);
275        }
276        if (value != null) {
277          return value.equals(markup);
278        }
279        for (String value : values) {
280            if (value.equals(markup)) {
281              return true;
282            }
283          }
284          return false;
285      }
286    
287      @Override
288      public String toString() {
289        if (value != null) {
290          return value;
291        }
292        if (values == null) {
293          return "null";
294        }
295        return Arrays.toString(values);
296      }
297    }