View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.tobago.renderkit;
21  
22  import org.apache.myfaces.tobago.component.SupportsAccessKey;
23  import org.apache.myfaces.tobago.component.UISelectBooleanCheckbox;
24  import org.apache.myfaces.tobago.config.TobagoConfig;
25  import org.apache.myfaces.tobago.internal.util.Deprecation;
26  import org.slf4j.Logger;
27  import org.slf4j.LoggerFactory;
28  
29  import javax.faces.context.FacesContext;
30  import java.util.Locale;
31  
32  public final class LabelWithAccessKey {
33  
34    private static final Logger LOG = LoggerFactory.getLogger(LabelWithAccessKey.class);
35  
36    public static final char INDICATOR = '_';
37    private static final String ESCAPED_INDICATOR = "__";
38  
39    private final String label;
40    private final Character accessKey;
41    private final int pos;
42  
43    /**
44     * @deprecated since 2.0.0. This is a workaround.
45     */
46    @Deprecated
47    public LabelWithAccessKey(final String label) {
48      this(new SupportsAccessKey() {
49        @Override
50        public Character getAccessKey() {
51          return null;
52        }
53  
54        @Override
55        public String getLabel() {
56          return label;
57        }
58      });
59    }
60  
61    public LabelWithAccessKey(final SupportsAccessKey component) {
62  
63      String label0;
64      Character accessKey0;
65      int pos0 = -1;
66  
67      label0 = component.getLabel();
68  
69      // compatibility since TOBAGO-1093
70      if (component instanceof UISelectBooleanCheckbox) {
71        final String itemLabel = ((UISelectBooleanCheckbox) component).getItemLabel();
72        if (itemLabel != null) {
73          label0 = itemLabel;
74        }
75      }
76  
77      accessKey0 = component.getAccessKey();
78      if (accessKey0 != null) {
79        accessKey0 = Character.toLowerCase(accessKey0);
80        if (!isPermitted(accessKey0)) {
81          LOG.warn("Ignoring illegal access key: " + accessKey0);
82          accessKey0 = null;
83        }
84      }
85  
86      boolean auto = TobagoConfig.getInstance(FacesContext.getCurrentInstance()).isAutoAccessKeyFromLabel();
87      // try to find the accessKey from the label if
88      // a) it's configured to do that AND
89      // b) there wasn't an accessKey defined in the component AND
90      // c) there is a label
91      if (auto && component.getAccessKey() == null && label0 != null) {
92  
93        final int first = label0.indexOf(INDICATOR);
94        if (first > -1) {
95          final char[] chars = label0.toCharArray();
96          int j = first;
97          for (int i = first; i < chars.length; i++) {
98            if (chars[i] == INDICATOR) {
99              if (i + 1 < chars.length) {
100               i++; // ignore the first one
101               chars[j] = chars[i];
102               if (chars[i] != INDICATOR) {
103                 if (accessKey0 == null) {
104                   pos0 = j;
105                   accessKey0 = Character.toLowerCase(chars[i]);
106                   if (!isPermitted(accessKey0)) {
107                     LOG.warn("Ignoring illegal access key: " + accessKey0);
108                     accessKey0 = null;
109                     pos0 = -1;
110                   }
111                 }
112               }
113               j++;
114             } else {
115               LOG.warn("'" + INDICATOR + "' in label is last char, this is not allowed label='" + label0 + "'.");
116             }
117           } else {
118             chars[j] = chars[i];
119             j++;
120           }
121         }
122         label0 = new String(chars, 0, j);
123       }
124 
125     } else {
126       if (accessKey0 != null && label0 != null) {
127         pos0 = label0.toLowerCase(Locale.ENGLISH).indexOf(accessKey0);
128       }
129     }
130 
131     label = label0;
132     accessKey = accessKey0;
133     pos = pos0;
134   }
135 
136 /*
137   private void findIndicator(final String label, int index, int escapedIndicatorCount) {
138 
139     index = label.indexOf(INDICATOR, index);
140     if (index == -1) {
141       text = label;
142     } else {
143       if (index == label.length() - 1) {
144         LOG.warn(INDICATOR + " in label is last char, this is not allowed"
145             + "label='" + label + "'.");
146         text = label.substring(0, label.length() - 1);
147         pos = -1;
148       } else if (label.charAt(index + 1) == INDICATOR) {
149         escapedIndicatorCount++;
150         findIndicator(label, index + 2, escapedIndicatorCount);
151       } else {
152         text = label.substring(0, index)
153             + label.substring(index + 1);
154         setAccessKey(text.charAt(index));
155         pos = index - escapedIndicatorCount;
156       }
157     }
158   }
159 */
160 
161   /**
162    * @deprecated since 2.0.0. Attributes are final now, use a new instance.
163    */
164   @Deprecated
165   public void setup(final String label) {
166     Deprecation.LOG.error("Ignoring label: " + label);
167   }
168 
169   /**
170    * @deprecated since 2.0.0. Attributes are final now, use a new instance.
171    */
172   @Deprecated
173   public void reset() {
174     Deprecation.LOG.error("Ignoring reset.");
175   }
176 
177   /**
178    * @deprecated since 2.0.0. Please use {@link #getLabel()}.
179    */
180   @Deprecated
181   public String getText() {
182     return label;
183   }
184 
185   public String getLabel() {
186     return label;
187   }
188 
189   public Character getAccessKey() {
190     return accessKey;
191   }
192 
193   public int getPos() {
194     return pos;
195   }
196 
197   /**
198    * @deprecated since 2.0.0. Attributes are final now, use a new instance.
199    */
200   @Deprecated
201   public void setText(final String text) {
202     Deprecation.LOG.error("Ignoring label: " + text);
203   }
204 
205   /**
206    * @deprecated since 2.0.0. Attributes are final now, use a new instance.
207    */
208   @Deprecated
209   public void setAccessKey(Character accessKey) {
210     Deprecation.LOG.error("Ignoring accessKey: " + accessKey);
211   }
212 
213   /**
214    * Ensures, that no illegal character will be write out.
215    * (If this is changed from only allowing normal letters and numbers, the renderers may change the escaping)
216    */
217   private boolean isPermitted(final Character accessKey) {
218     return accessKey == null || accessKey >= 'a' && accessKey <= 'z' || accessKey >= '0' && accessKey <= '9';
219   }
220 }