1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.el.unified.resolver;
20
21 import java.beans.FeatureDescriptor;
22 import static java.lang.String.format;
23 import java.lang.reflect.Field;
24 import java.lang.reflect.Modifier;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.Iterator;
29 import java.util.Map;
30 import java.util.concurrent.ConcurrentHashMap;
31 import static java.util.logging.Level.FINE;
32 import java.util.logging.Logger;
33 import javax.el.ELContext;
34 import javax.el.ELException;
35 import javax.el.ELResolver;
36 import javax.el.PropertyNotFoundException;
37 import javax.el.PropertyNotWritableException;
38 import javax.faces.component.UIImportConstants;
39 import javax.faces.component.UIViewRoot;
40 import javax.faces.context.FacesContext;
41 import javax.faces.view.ViewMetadata;
42 import org.apache.myfaces.shared.util.ClassUtils;
43 import org.apache.myfaces.view.facelets.FaceletViewDeclarationLanguage;
44
45
46
47
48 public final class ImportConstantsELResolver extends ELResolver
49 {
50 private static final String ERROR_MISSING_CLASS = "Cannot find type '%s' in classpath.";
51 private static final String ERROR_FIELD_ACCESS = "Cannot access constant field '%s' of type '%s'.";
52
53 private static final String IMPORT_CONSTANTS = "oam.importConstants";
54
55 private Map<String, Map<String, Object> > constantsTypeMap = new ConcurrentHashMap<String, Map<String, Object> >();
56
57 @Override
58 public Object getValue(final ELContext context, final Object base,
59 final Object property)
60 throws NullPointerException, PropertyNotFoundException, ELException
61 {
62 if (base != null)
63 {
64 return null;
65 }
66 if (property == null)
67 {
68 throw new PropertyNotFoundException();
69 }
70 if (!(property instanceof String))
71 {
72 return null;
73 }
74
75 final FacesContext facesContext = facesContext(context);
76 if (facesContext == null)
77 {
78 return null;
79 }
80
81 UIViewRoot viewRoot = facesContext.getViewRoot();
82 if (viewRoot == null)
83 {
84 return null;
85 }
86
87 Map<String, String> importConstantsMap = (Map<String, String>)
88 viewRoot.getTransientStateHelper().getTransient(IMPORT_CONSTANTS);
89 if (importConstantsMap == null)
90 {
91 Collection<UIImportConstants> constants = ViewMetadata.getImportConstants(viewRoot);
92 if (constants != null && !constants.isEmpty())
93 {
94 importConstantsMap = new HashMap<String, String>();
95 for (UIImportConstants c : constants)
96 {
97 String var = c.getVar();
98 String type = c.getType();
99 if (var == null)
100 {
101 int innerClass = type.lastIndexOf('$');
102 int outerClass = type.lastIndexOf('.');
103 var = type.substring(Math.max(innerClass, outerClass) + 1);
104 }
105 importConstantsMap.put(var, type);
106 }
107 }
108 else
109 {
110 importConstantsMap = Collections.emptyMap();
111 }
112 if (!FaceletViewDeclarationLanguage.isBuildingViewMetadata(facesContext))
113 {
114 viewRoot.getTransientStateHelper().putTransient(IMPORT_CONSTANTS, importConstantsMap);
115 }
116 }
117
118 if (importConstantsMap != null && !importConstantsMap.isEmpty())
119 {
120 String type = importConstantsMap.get((String)property);
121 if (type != null)
122 {
123 Map<String, Object> constantsMap = constantsTypeMap.get(type);
124 if (constantsMap == null)
125 {
126 constantsMap = collectConstants(type);
127 constantsTypeMap.put(type, constantsMap);
128 }
129 if (constantsMap != null && !constantsMap.isEmpty())
130 {
131 context.setPropertyResolved(true);
132 return constantsMap;
133 }
134 }
135 }
136 return null;
137 }
138
139 @Override
140 public Class<?> getType(final ELContext context, final Object base,
141 final Object property)
142 throws NullPointerException, PropertyNotFoundException, ELException
143 {
144 return null;
145 }
146
147 @Override
148 public void setValue(ELContext elc, Object o, Object o1, Object o2)
149 throws NullPointerException, PropertyNotFoundException, PropertyNotWritableException, ELException
150 {
151
152 }
153
154 @Override
155 public boolean isReadOnly(final ELContext context, final Object base,
156 final Object property)
157 throws NullPointerException, PropertyNotFoundException, ELException
158 {
159 return false;
160 }
161
162 @Override
163 public Iterator<FeatureDescriptor> getFeatureDescriptors(final ELContext context, final Object base)
164 {
165 return null;
166 }
167
168 @Override
169 public Class<?> getCommonPropertyType(final ELContext context, final Object base)
170 {
171 return base == null ? Object.class : null;
172 }
173
174
175 private static FacesContext facesContext(final ELContext context)
176 {
177 return (FacesContext) context.getContext(FacesContext.class);
178 }
179
180
181
182
183
184
185
186
187 private static Map<String, Object> collectConstants(final String type)
188 {
189 Map<String, Object> constants = new HashMap<String, Object>();
190
191 for (Field field : toClass(type).getFields())
192 {
193 if (isPublicStaticFinal(field))
194 {
195 try
196 {
197 constants.put(field.getName(), field.get(null));
198 }
199 catch (Exception e)
200 {
201 throw new IllegalArgumentException(format(ERROR_FIELD_ACCESS, type, field.getName()), e);
202 }
203 }
204 }
205
206 return constants;
207 }
208
209
210
211
212
213
214
215
216 static Class<?> toClass(String type)
217 {
218
219 try
220 {
221 return ClassUtils.classForName(type);
222 }
223 catch (ClassNotFoundException e)
224 {
225
226
227 int i = type.lastIndexOf('.');
228
229 if (i > 0)
230 {
231 try
232 {
233 return toClass(new StringBuilder(type).replace(i, i + 1, "$").toString());
234 }
235 catch (Exception ignore)
236 {
237 Logger.getLogger(ImportConstantsELResolver.class.getName()).log(
238 FINE, "Ignoring thrown exception; previous exception will be rethrown instead.", ignore);
239
240 }
241 }
242
243 throw new IllegalArgumentException(format(ERROR_MISSING_CLASS, type), e);
244 }
245 }
246
247
248
249
250
251
252
253 private static boolean isPublicStaticFinal(Field field)
254 {
255 int modifiers = field.getModifiers();
256 return Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers);
257 }
258
259 }