001: package org.vraptor.introspector;
002:
003: import java.lang.reflect.Array;
004: import java.util.ArrayList;
005: import java.util.HashMap;
006: import java.util.List;
007: import java.util.Map;
008: import java.util.Set;
009: import java.util.TreeSet;
010: import java.util.Map.Entry;
011:
012: import org.apache.log4j.Logger;
013: import org.vraptor.LogicRequest;
014: import org.vraptor.annotations.In;
015: import org.vraptor.component.Clazz;
016: import org.vraptor.component.ComponentInstantiationException;
017: import org.vraptor.component.FieldAnnotation;
018: import org.vraptor.component.Outjectable;
019: import org.vraptor.converter.ConversionException;
020: import org.vraptor.converter.ConverterManager;
021: import org.vraptor.i18n.ValidationMessage;
022: import org.vraptor.reflection.GettingException;
023: import org.vraptor.reflection.JPathExecutor;
024: import org.vraptor.reflection.MethodInvocationException;
025: import org.vraptor.reflection.ReflectionUtil;
026: import org.vraptor.reflection.SettingException;
027: import org.vraptor.scope.RequestContext;
028: import org.vraptor.scope.ScopeType;
029:
030: /**
031: * The default introspector implementation.
032: *
033: * @author Guilherme Silveira
034: */
035: public class BasicIntrospector implements Introspector {
036:
037: private static final Logger LOG = Logger
038: .getLogger(BasicIntrospector.class);
039:
040: private final KeyExtractor keyExtractor = new KeyExtractor();
041:
042: private BeanProvider beanProvider;
043:
044: private final Map<Class, Clazz> clazzDirectory;
045:
046: public BasicIntrospector() {
047: beanProvider = new WebBeanProvider();
048: clazzDirectory = new HashMap<Class, Clazz>();
049: }
050:
051: private Set<Parameter> addAll(Set<String> keys) {
052: Set<Parameter> ts = new TreeSet<Parameter>();
053: for (String key : keys) {
054: ts.add(new Parameter(key));
055: }
056: return ts;
057: }
058:
059: @SuppressWarnings("unchecked")
060: public List<ValidationMessage> readParameters(
061: List<ReadParameter> parametersToRead, Object component,
062: LogicRequest logicRequest,
063: ConverterManager converterManager,
064: Object[] methodParamObjects) throws SettingException {
065:
066: JPathExecutor executor = new JPathExecutor(converterManager,
067: logicRequest, methodParamObjects, component);
068: RequestContext request = logicRequest.getRequestContext();
069:
070: Map<String, Object> parameters = request.getParameterMap();
071: List<ValidationMessage> problems = new ArrayList<ValidationMessage>();
072:
073: out: for (Parameter p : addAll(parameters.keySet())) {
074: String parameter = p.getKey();
075: for (ReadParameter read : parametersToRead) {
076: if (!p.matches(read.getKey())) {
077: continue;
078: }
079: if (LOG.isDebugEnabled()) {
080: LOG
081: .debug("Parameter " + parameter
082: + " will be used on field "
083: + read.getKey());
084: }
085: try {
086: // TODO: dont do this, cache it in request instead (or
087: // use the internal request itself)
088: executor.set(p.getPath(), singleValue(parameters
089: .get(parameter)), arrayValue(parameters
090: .get(parameter)), read);
091: } catch (ConversionException e) {
092: // validation problem...
093: if (LOG.isDebugEnabled()) {
094: LOG.debug(e.getMessage(), e);
095: }
096: ValidationMessage msg = e.getI18NMessage();
097: msg.setPath(parameter);
098: problems.add(msg);
099: }
100: continue out;
101: }
102: if (LOG.isDebugEnabled()) {
103: LOG.debug("Parameter not used: " + parameter);
104: }
105: }
106:
107: return problems;
108:
109: }
110:
111: private String[] arrayValue(Object val) {
112: if (val.getClass().isArray()) {
113: return (String[]) val;
114: }
115: return new String[] { (String) val };
116: }
117:
118: private String singleValue(Object val) {
119: if (val.getClass().isArray()) {
120: return (String) Array.get(val, 0);
121: }
122: return (String) val;
123: }
124:
125: public void inject(List<FieldAnnotation<In>> inAnnotations,
126: Object component, LogicRequest request)
127: throws ComponentInstantiationException, SettingException {
128:
129: for (FieldAnnotation<In> info : inAnnotations) {
130: In in = info.getAnnotation();
131: String key = keyExtractor.extractInKey(info);
132: Object value = in.scope().getContext(request).getAttribute(
133: key);
134: setFieldForInjection(component, info, in, key, value);
135: }
136:
137: }
138:
139: private void setFieldForInjection(Object component,
140: FieldAnnotation<In> info, In in, String key, Object value)
141: throws ComponentInstantiationException, SettingException {
142:
143: if (LOG.isDebugEnabled()) {
144: LOG.debug("Injecting with key " + key + " from "
145: + in.scope());
146: }
147:
148: if (value == null) {
149: if (in.create()) {
150: if (LOG.isDebugEnabled()) {
151: LOG.debug("Trying to create "
152: + info.getField().getType().getName());
153: }
154: value = getClazz(info.getField().getType())
155: .newInstance();
156: } else if (in.required()) {
157: throw new SettingException(
158: "Unable to fill inject value for field "
159: + info.getField().getName());
160: } else {
161: return;
162: }
163: }
164:
165: ReflectionUtil.setField(component, info.getField(), value);
166: }
167:
168: /**
169: * Returns the clazz instance for the specified type.
170: * @param type the type
171: * @return the clazz instance
172: * @since 2.3.2
173: */
174: private Clazz getClazz(Class<?> type) {
175: // TODO is sync really worth it?
176: if (!clazzDirectory.containsKey(type)) {
177: clazzDirectory.put(type, new Clazz(type));
178: }
179: return clazzDirectory.get(type);
180: }
181:
182: public void outject(LogicRequest logicRequest, Object component,
183: Outjectable type) throws GettingException,
184: MethodInvocationException {
185: for (ScopeType scope : ScopeType.values()) {
186: Map<String, Object> values = type.getOutjectedValues(
187: component, scope);
188: for (Entry<String, Object> value : values.entrySet()) {
189: if (LOG.isDebugEnabled()) {
190: LOG.debug("Outjecting key " + value.getKey()
191: + " at " + scope);
192: }
193: scope.getContext(logicRequest).setAttribute(
194: value.getKey(), value.getValue());
195: }
196: }
197: }
198:
199: public BeanProvider getBeanProvider() {
200: return beanProvider;
201: }
202:
203: public void setBeanProvider(BeanProvider beanProvider) {
204: this.beanProvider = beanProvider;
205: }
206:
207: }
|