001: /*
002: * Created on 2 Aug 2007
003: */
004: package uk.org.ponder.mapping;
005:
006: import java.util.ArrayList;
007: import java.util.HashMap;
008: import java.util.List;
009: import java.util.Map;
010:
011: import uk.org.ponder.arrayutil.ListUtil;
012: import uk.org.ponder.arrayutil.MapUtil;
013: import uk.org.ponder.beanutil.BeanLocator;
014: import uk.org.ponder.beanutil.BeanResolver;
015: import uk.org.ponder.beanutil.PathUtil;
016: import uk.org.ponder.reflect.ReflectUtils;
017: import uk.org.ponder.util.Logger;
018:
019: /**
020: * A registry of {@link DataConverter} elements, capable of resolving them into
021: * the canonical forms of {@link DARReshaper}s and {@link BeanResolver}s, and
022: * assessing for any proposed operation which of the converters are applicable.
023: * </p>
024: * The registry of converters is assumed static and will be fully evaluated when
025: * this bean starts up.
026: *
027: * @author Antranig Basman (amb26@ponder.org.uk)
028: *
029: */
030: public class DataConverterRegistry {
031: private static final Class ROOT_CLASS = BeanLocator.class;
032: private List converters;
033:
034: public void setBeanLocator(BeanLocator beanLocator) {
035: this .beanLocator = beanLocator;
036: }
037:
038: public void setConverters(List converters) {
039: this .converters = converters;
040: }
041:
042: private BeanLocator beanLocator;
043:
044: private Map byClass = new HashMap();
045:
046: public void init() {
047: if (converters == null)
048: return;
049: for (int i = 0; i < converters.size(); ++i) {
050: DataConverter converter = (DataConverter) converters.get(i);
051: if (converter.getConverterEL() != null) {
052: Object conv = beanLocator.locateBean(converter
053: .getConverterEL());
054: converter.setConverter(conv);
055: }
056: Class key = converter.getTargetClass();
057: if (key == null)
058: key = ROOT_CLASS;
059: MapUtil.putMultiMap(byClass, key, converter);
060: }
061: }
062:
063: private static class ConverterCandidate {
064: public DataConverter converter;
065: public String[] segments;
066: public int consumed = 0;
067:
068: public ConverterCandidate(DataConverter converter) {
069: this .converter = converter;
070: if (converter.getTargetPath() != null) {
071: segments = PathUtil
072: .splitPath(converter.getTargetPath());
073: }
074: }
075: }
076:
077: public Object fetchConverter(ShellInfo shellinfo) {
078: String[] segments = shellinfo.segments;
079: List candidates = new ArrayList();
080: List rootconverters = fetchConverters(ListUtil
081: .instance(ROOT_CLASS));
082: accreteCandidates(candidates, rootconverters);
083: for (int i = 0; i < segments.length; ++i) {
084: Object shell = (i + 1) >= shellinfo.shells.length ? null
085: : shellinfo.shells[i + 1];
086: List clazzes = shell == null ? new ArrayList()
087: : ReflectUtils.getSuperclasses(shell.getClass());
088: filterCandidates(candidates, segments[i]);
089: List converters = fetchConverters(clazzes);
090: accreteCandidates(candidates, converters);
091: }
092: if (candidates.size() == 0)
093: return null;
094: if (candidates.size() > 1) {
095: Logger.log
096: .warn("Warning: duplicate DataConverter candidates discovered for EL path "
097: + PathUtil.buildPath(shellinfo.segments)
098: + " only the last (probably the most specific) entry will be applied.");
099: }
100: ConverterCandidate candidate = (ConverterCandidate) candidates
101: .get(candidates.size() - 1);
102: return candidate.converter.getConverter();
103: }
104:
105: private void filterCandidates(List candidates, String segment) {
106: for (int i = candidates.size() - 1; i >= 0; --i) {
107: ConverterCandidate candidate = (ConverterCandidate) candidates
108: .get(i);
109: if (candidate.segments != null
110: && candidate.consumed < candidate.segments.length) {
111: String matchseg = candidate.segments[candidate.consumed];
112: candidate.consumed++;
113: if (matchseg.equals("*") || matchseg.equals(segment))
114: continue;
115: }
116: candidates.remove(i);
117: }
118: }
119:
120: private static void accreteCandidates(List candidates,
121: List converters) {
122: for (int i = 0; i < converters.size(); ++i) {
123: DataConverter converter = (DataConverter) converters.get(i);
124: candidates.add(new ConverterCandidate(converter));
125: }
126: }
127:
128: /** Returns all converters registered for the supplied classes * */
129: private List fetchConverters(List clazzes) {
130: ArrayList togo = new ArrayList();
131: for (int i = 0; i < clazzes.size(); ++i) {
132: List things = (List) byClass.get(clazzes.get(i));
133: if (things != null) {
134: togo.addAll(things);
135: }
136: }
137: return togo;
138: }
139:
140: // If there is a DARReceiver in the way, we will not be able to process any
141: // by-Class matches - however, we *will* be able to process by-Path matches.
142: // the list of shells will therefore be incomplete.
143: public DARReshaper fetchReshaper(ShellInfo shellinfo) {
144: Object converter = fetchConverter(shellinfo);
145: if (converter != null) {
146: return ConverterConverter.toReshaper(converter);
147: } else
148: return null;
149: }
150:
151: }
|