package net.nightwhistler.htmlspanner; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.util.Log; import net.nightwhistler.htmlspanner.css.CompiledRule; import net.nightwhistler.htmlspanner.style.Style; import org.htmlcleaner.TagNode; import java.util.*; /** * Simple stack structure that Spans can be pushed on. * * Handles the lookup and application of CSS styles. * * @author Alex Kuiper */ public class SpanStack { private Stack<SpanCallback> spanItemStack = new Stack<SpanCallback>(); private Set<CompiledRule> rules = new HashSet<CompiledRule>(); private Map<TagNode, List<CompiledRule>> lookupCache = new HashMap<TagNode, List<CompiledRule>>(); public void registerCompiledRule(CompiledRule rule) { this.rules.add( rule ); } public Style getStyle( TagNode node, Style baseStyle ) { if ( ! lookupCache.containsKey(node) ) { Log.v("SpanStack", "Looking for matching CSS rules for node: " + "<" + node.getName() + " id='" + option(node.getAttributeByName("id")) + "' class='" + option(node.getAttributeByName("class")) + "'>"); List<CompiledRule> matchingRules = new ArrayList<CompiledRule>(); for ( CompiledRule rule: rules ) { if ( rule.matches(node)) { matchingRules.add(rule); } } Log.v("SpanStack", "Found " + matchingRules.size() + " matching rules."); lookupCache.put(node, matchingRules); } Style result = baseStyle; for ( CompiledRule rule: lookupCache.get(node) ) { Log.v( "SpanStack", "Applying rule " + rule ); Style original = result; result = rule.applyStyle(result); Log.v("SpanStack", "Original style: " + original ); Log.v("SpanStack", "Resulting style: " + result); } return result; } private static String option( String s ) { if ( s == null ) { return ""; } else { return s; } } public void pushSpan( final Object span, final int start, final int end ) { if ( end > start ) { SpanCallback callback = new SpanCallback() { @Override public void applySpan(HtmlSpanner spanner, SpannableStringBuilder builder) { builder.setSpan(span, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } }; spanItemStack.push(callback); } else { Log.d( "SpanStack", "refusing to put span of type " + span.getClass().getSimpleName() + " and length " + (end - start) ); } } public void pushSpan( SpanCallback callback ) { spanItemStack.push(callback); } public void applySpans(HtmlSpanner spanner, SpannableStringBuilder builder ) { while ( ! spanItemStack.isEmpty() ) { spanItemStack.pop().applySpan(spanner, builder); } } }