package org.robobinding.attribute; import java.util.Collection; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.robobinding.PendingAttributesForView; import org.robobinding.PendingAttributesForViewImpl; import org.robobinding.PredefinedPendingAttributesForView; import org.robobinding.util.Lists; import org.robobinding.util.Maps; import org.robobinding.util.Objects; import android.content.Context; import android.view.View; /** * * @since 1.0 * @version $Revision: 1.0 $ * @author Robert Taylor */ public class PredefinedMappingsAttribute extends AbstractAttribute { private static final String MAPPING_PATTERN = "(\\w+)\\.(\\w+):($?\\{\\w+\\})"; private static final Pattern MAPPING_COMPILED_PATTERN = Pattern.compile(MAPPING_PATTERN); private static final Pattern MAPPING_ATTRIBUTE_COMPILED_PATTERN = Pattern.compile("^\\[" + MAPPING_PATTERN + "(?:(,\\s?)" + MAPPING_PATTERN + ")*\\]$"); private final String attributeValue; public PredefinedMappingsAttribute(String name, String value) { super(name); this.attributeValue = value; if (!MAPPING_ATTRIBUTE_COMPILED_PATTERN.matcher(value).matches()) throw new MalformedAttributeException(getName(), "Mapping attribute value: " + value + " contains invalid syntax."); } public Collection<PredefinedPendingAttributesForView> getViewMappings(Context context) { return new ItemMappingParser().parse(getName(), attributeValue, context).getPredefinedPendingAttributesForViewGroup(); } private static class ItemMappingParser { public ViewMappings parse(String name, String value, Context context) { Matcher matcher = MAPPING_COMPILED_PATTERN.matcher(value); ViewMappings viewMappings = new ViewMappings(); while (matcher.find()) { String viewIdString = matcher.group(1); String nestedAttributeName = matcher.group(2); String nestedAttributeValue = matcher.group(3); int viewId = context.getResources().getIdentifier(viewIdString, "id", "android"); if (viewId == 0) throw new MalformedAttributeException(name, "View with id name: " + viewIdString + " in package: android could not be found"); viewMappings.add(viewId, nestedAttributeName, nestedAttributeValue); } return viewMappings; } } private static class ViewMappings { private Map<Integer, ViewMapping> viewMappingsMap = Maps.newHashMap(); void add(int viewId, String attributeName, String attributeValue) { ViewMapping existingViewMapping = viewMappingsMap.get(viewId); if (existingViewMapping != null) { existingViewMapping.add(attributeName, attributeValue); } else { viewMappingsMap.put(viewId, new ViewMapping(viewId, attributeName, attributeValue)); } } public Collection<PredefinedPendingAttributesForView> getPredefinedPendingAttributesForViewGroup() { return Lists.<PredefinedPendingAttributesForView> newArrayList(viewMappingsMap.values()); } } static class ViewMapping implements PredefinedPendingAttributesForView { int viewId; Map<String, String> bindingAttributes = Maps.newHashMap(); public ViewMapping(int viewId, String attributeName, String attributeValue) { this.viewId = viewId; this.add(attributeName, attributeValue); } public void add(String attributeName, String attributeValue) { this.bindingAttributes.put(attributeName, attributeValue); } @Override public PendingAttributesForView createPendingAttributesForView(View rootView) { View childView = rootView.findViewById(viewId); if(childView == null) { String resName = rootView.getResources().getResourceName(viewId); throw new RuntimeException("predefined mapping viewId '"+resName+"' cannot be found"); } return new PendingAttributesForViewImpl(childView, bindingAttributes); } @Override public boolean equals(Object other) { if (this == other) return true; if (!(other instanceof ViewMapping)) return false; final ViewMapping that = (ViewMapping) other; return Objects.equal(bindingAttributes, that.bindingAttributes) && Objects.equal(viewId, that.viewId); } @Override public int hashCode() { return Objects.hashCode(bindingAttributes, viewId); } } public static PredefinedMappingsAttribute nullAttribute(String attributeName) { return new PredefinedMappingsAttribute(attributeName, "[text1.text:{property}]") { @Override public Collection<PredefinedPendingAttributesForView> getViewMappings(Context context) { return Lists.newArrayList(); } }; } }