All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.daisy.pipeline.braille.css.impl.BrailleCssTreeBuilder Maven / Gradle / Ivy

The newest version!
package org.daisy.pipeline.braille.css.impl;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import cz.vutbr.web.css.Declaration;
import cz.vutbr.web.css.Rule;
import cz.vutbr.web.css.RuleBlock;
import cz.vutbr.web.css.RuleMargin;
import cz.vutbr.web.css.RulePage;
import cz.vutbr.web.css.Selector;
import cz.vutbr.web.css.Selector.Combinator;
import cz.vutbr.web.css.Selector.PseudoClass;
import cz.vutbr.web.css.Selector.SelectorPart;

import org.daisy.braille.css.AnyAtRule;
import org.daisy.braille.css.InlineStyle;
import org.daisy.braille.css.InlineStyle.RuleMainBlock;
import org.daisy.braille.css.InlineStyle.RuleRelativeBlock;
import org.daisy.braille.css.InlineStyle.RuleRelativeHyphenationResource;
import org.daisy.braille.css.InlineStyle.RuleRelativePage;
import org.daisy.braille.css.InlineStyle.RuleRelativeVolume;
import org.daisy.braille.css.SelectorImpl.PseudoElementImpl;
import org.daisy.braille.css.RuleCounterStyle;
import org.daisy.braille.css.RuleHyphenationResource;
import org.daisy.braille.css.RuleTextTransform;
import org.daisy.braille.css.RuleVolume;
import org.daisy.braille.css.RuleVolumeArea;

public final class BrailleCssTreeBuilder {

	private BrailleCssTreeBuilder() {}

	public static class Style implements Comparator {

		List declarations;
		SortedMap nestedStyles; // sorted by key

		public int compare(String selector1, String selector2) {
			if (selector1.startsWith("&") && !selector2.startsWith("&"))
				return 1;
			else if (!selector1.startsWith("&") && selector2.startsWith("&"))
				return -1;
			else
				return selector1.compareTo(selector2);
		}

		private Style add(Declaration declaration) {
			if (declaration != null) {
				if (this.declarations == null) this.declarations = new ArrayList();
				declarations.add(declaration);
			}
			return this;
		}

		private Style add(Iterable declarations) {
			if (declarations != null)
				for (Declaration d : declarations)
					add(d);
			return this;
		}

		Style add(String selector, Style nestedStyle) {
			if (this.nestedStyles == null) this.nestedStyles = new TreeMap(this);
			if (selector == null)
				add(nestedStyle);
			if (this.nestedStyles.containsKey(selector))
				this.nestedStyles.get(selector).add(nestedStyle);
			else
				this.nestedStyles.put(selector, nestedStyle);
			return this;
		}

		Style add(String[] selector, Style nestedStyle) {
			if (selector.length == 0)
				return this;
			for (int i = selector.length - 1; i > 0; i--)
				nestedStyle = new Style().add(selector[i], nestedStyle);
			return add(selector[0], nestedStyle);
		}

		private Style add(Style style) {
			add(style.declarations);
			if (style.nestedStyles != null)
				for (Map.Entry e : style.nestedStyles.entrySet())
					add(e.getKey(), e.getValue());
			return this;
		}

		static Style of(RulePage page) {
			// assumed to be anonymous page
			Style style = new Style();
			for (Rule r : page)
				if (r instanceof Declaration)
					style.add((Declaration)r);
				else if (r instanceof RuleMargin)
					style.add("@" + ((RuleMargin)r).getMarginArea(),
					          new Style().add((List)r));
				else
					throw new RuntimeException("coding error");
			String pseudo = page.getPseudo();
			return pseudo == null
				? style
				: new Style().add("&:" + pseudo, style);
		}

		private static Style of(RuleVolumeArea volumeArea) {
			Style style = new Style();
			for (Rule r : volumeArea)
				if (r instanceof Declaration)
					style.add((Declaration)r);
				else if (r instanceof RulePage)
					style.add("@page", Style.of((RulePage)r));
				else
					throw new RuntimeException("coding error");
			return style;
		}

		private static Style of(RuleVolume volume) {
			Style style = new Style();
			for (Rule r : volume)
				if (r instanceof Declaration)
					style.add((Declaration)r);
				else if (r instanceof RuleVolumeArea)
					style.add("@" + ((RuleVolumeArea)r).getVolumeArea().value, Style.of((RuleVolumeArea)r));
				else
					throw new RuntimeException("coding error");
			String pseudo = volume.getPseudo();
			return pseudo == null
				? style
				: new Style().add("&:" + pseudo, style);
		}

		private static Style of(RuleHyphenationResource rule) {
			return new Style().add("&:lang("
			                       + BrailleCssSerializer.serializeLanguageRanges(rule.getLanguageRanges())
			                       + ")",
			                       new Style().add((List)rule));
		}

		private static Style of(AnyAtRule rule) {
			Style style = new Style();
			for (Rule r : rule)
				if (r instanceof Declaration)
					style.add((Declaration)r);
				else if (r instanceof AnyAtRule)
					style.add("@" + ((AnyAtRule)r).getName(), Style.of((AnyAtRule)r));
				else
					throw new RuntimeException("coding error");
			return style;
		}

		public static Style of(InlineStyle inlineStyle) {
			Style style = new Style();
			for (RuleBlock rule : inlineStyle) {
				if (rule instanceof RuleMainBlock)
					// Note that the declarations have not been transformed by BrailleCSSDeclarationTransformer yet
					style.add((List)rule);
				else if (rule instanceof RuleRelativeBlock) {
					String[] selector = serializeSelector(((RuleRelativeBlock)rule).getSelector());
					Style decls = new Style(); {
						for (Rule r : (RuleRelativeBlock)rule)
							if (r instanceof Declaration)
								decls.add((Declaration)r);
							else if (r instanceof RulePage)
								decls.add("@page", Style.of((RulePage)r));
							else
								throw new RuntimeException("coding error");
					}
					style.add(selector, decls); }
				else if (rule instanceof RulePage)
					style.add("@page", Style.of((RulePage)rule));
				else if (rule instanceof RuleVolume)
					style.add("@volume", Style.of((RuleVolume)rule));
				else if (rule instanceof RuleTextTransform) {
					String name = ((RuleTextTransform)rule).getName();
					Style textTransform = new Style().add((List)rule);
					if (name == null)
						style.add("@text-transform", textTransform);
					else
						style.add("@text-transform", new Style().add("& " + name, textTransform)); }
				else if (rule instanceof RuleHyphenationResource)
					style.add("@hyphenation-resource", Style.of((RuleHyphenationResource)rule));
				else if (rule instanceof RuleCounterStyle) {
					String name = ((RuleCounterStyle)rule).getName();
					Style counterStyle = new Style().add((List)rule);
					style.add("@counter-style", new Style().add("& " + name, counterStyle)); }
				else if (rule instanceof RuleMargin)
					style.add("@" + ((RuleMargin)rule).getMarginArea(),
					          new Style().add((List)rule));
				else if (rule instanceof RuleVolumeArea)
					style.add("@" + ((RuleVolumeArea)rule).getVolumeArea().value,
					          Style.of((RuleVolumeArea)rule));
				else if (rule instanceof RuleRelativePage)
					style.add(Style.of(((RuleRelativePage)rule).asRulePage()));
				else if (rule instanceof RuleRelativeVolume)
					style.add(Style.of(((RuleRelativeVolume)rule).asRuleVolume()));
				else if (rule instanceof RuleRelativeHyphenationResource)
					style.add(Style.of(((RuleRelativeHyphenationResource)rule).asRuleHyphenationResource()));
				else if (rule instanceof AnyAtRule)
					style.add("@" + ((AnyAtRule)rule).getName(),
					          Style.of((AnyAtRule)rule));
				else
					throw new RuntimeException("coding error");
			}
			return style;
		}

		@Override
		public String toString() {
			return BrailleCssSerializer.toString(this);
		}
	}

	/* Split the selector parts and serialize */
	private static String[] serializeSelector(List combinedSelector) {
		List selector = new ArrayList<>();
		for (CombinatorSelectorPart part : flattenSelector(combinedSelector))
			selector.add("&" + part);
		return selector.toArray(new String[selector.size()]);
	}

	/* Convert a combined selector, which may contain pseudo element parts with other pseudo
	 * elements stacked onto them, into a flat list of selector parts and combinators */
	private static List flattenSelector(List combinedSelector) {
		List selector = new ArrayList<>();
		for (Selector s : combinedSelector)
			flattenSelector(selector, s);
		return selector;
	}

	private static void flattenSelector(List collect, Selector selector) {
		Combinator combinator = selector.getCombinator();
		for (SelectorPart part : selector) {
			flattenSelector(collect, combinator, part);
			combinator = null;
		}
	}

	private static void flattenSelector(List collect, Combinator combinator, SelectorPart part) {
		collect.add(new CombinatorSelectorPart(combinator, part));
		if (part instanceof PseudoElementImpl) {
			PseudoElementImpl pe = (PseudoElementImpl)part;
			if (!pe.getCombinedSelectors().isEmpty())
				for (Selector s: pe.getCombinedSelectors())
					flattenSelector(collect, s);
			else {
				if (!pe.getPseudoClasses().isEmpty())
					for (PseudoClass pc : pe.getPseudoClasses())
						collect.add(new CombinatorSelectorPart(null, pc));
				if (pe.hasStackedPseudoElement())
					flattenSelector(collect, null, pe.getStackedPseudoElement());
			}
		}
	}

	private static class CombinatorSelectorPart {
		final Combinator combinator;
		final SelectorPart selector;
		CombinatorSelectorPart(Combinator combinator, SelectorPart selector) {
			this.combinator = combinator;
			this.selector = selector;
		}
		@Override
		public String toString() {
			StringBuilder b = new StringBuilder();
			if (combinator != null)
				b.append(combinator.value());
			if (selector instanceof PseudoElementImpl) {
				PseudoElementImpl pe = (PseudoElementImpl)selector;
				b.append(":");
				if (!pe.isSpecifiedAsClass())
					b.append(":");
				b.append(pe.getName());
				String[] args = pe.getArguments();
				if (args.length > 0) {
					b.append("(");
					for (int i = 0; i < args.length; i++) {
						if (i > 0) b.append(", ");
						b.append(args[i]);
					}
					b.append(")");
				}
			} else
				b.append(selector);
			return b.toString();
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy