/******************************************************************************* * Copyright 2012-present Pixate, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.pixate.freestyle.styling; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.content.Context; import com.pixate.freestyle.PixateFreestyle; import com.pixate.freestyle.styling.animation.PXKeyframe; import com.pixate.freestyle.styling.media.PXMediaExpression; import com.pixate.freestyle.styling.media.PXMediaGroup; import com.pixate.freestyle.styling.parsing.PXStylesheetParser; import com.pixate.freestyle.util.IOUtil; import com.pixate.freestyle.util.PXLog; import com.pixate.freestyle.util.StringUtil; // TODO: Create container to hold PXStylesheet so parser can return that instead of having to subclass this from Symbol public class PXStylesheet { // @formatter:off public enum PXStyleSheetOrigin { APPLICATION, USER, VIEW, INLINE } // @formatter:on private static String TAG = PXStylesheet.class.getSimpleName(); // TODO: Use a parser pool since this is not thread safe now private static PXStylesheetParser PARSER = new PXStylesheetParser(); private static PXStylesheet currentApplicationStylesheet; private static PXStylesheet currentUserStylesheet; private static PXStylesheet currentViewStylesheet; private List<PXMediaGroup> mediaGroups; private PXMediaExpression activeMediaQuery; private PXMediaGroup activeMediaGroup; private Map<String, String> namespacePrefixMap; private Map<String, PXKeyframe> keyframesByName; private PXStyleSheetOrigin origin; private List<String> errors; private String filePath; public static PXStylesheet getStyleSheetFromSource(String source, PXStyleSheetOrigin origin) { return getStyleSheetFromSource(source, origin, null); } public static PXStylesheet getStyleSheetFromSource(String source, PXStyleSheetOrigin origin, String fileName) { PXStylesheet result = null; if (!StringUtil.isEmpty(source)) { result = PARSER.parse(source, origin, fileName); result.setErrors(PARSER.getErrors()); assignCurrentStylesheet(result, origin); } else { result = new PXStylesheet(origin); } return result; } public static PXStylesheet getStyleSheetFromFilePath(Context context, String aFilePath, PXStyleSheetOrigin origin) { try { String source = IOUtil.read(context.getAssets().open(aFilePath)); return getStyleSheetFromSource(source, origin, aFilePath); } catch (IOException e) { PXLog.e(TAG, "Error reading stylesheet from " + aFilePath); } return null; } // PUBLIC public PXStylesheet() { this(PXStyleSheetOrigin.APPLICATION); } public PXStylesheet(PXStyleSheetOrigin origin) { this.origin = origin; assignCurrentStylesheet(this, origin); } // Getters // TODO cache per context public List<PXRuleSet> getRuleSets(Context context) { List<PXRuleSet> combined = new ArrayList<PXRuleSet>(); if (mediaGroups != null) { for (PXMediaGroup group : mediaGroups) { if (group.matches(context)) { combined.addAll(group.getRuleSets()); } } } if (combined.isEmpty()) { return null; } return combined; } public List<PXRuleSet> getRuleSets(Object styleable, Context context) { List<PXRuleSet> combined = new ArrayList<PXRuleSet>(); if (mediaGroups != null) { for (PXMediaGroup group : mediaGroups) { if (group.matches(context)) { List<PXRuleSet> ruleSets = group.getRuleSets(styleable); if (ruleSets != null) { combined.addAll(ruleSets); } } } } if (combined.isEmpty()) { return null; } return combined; } public List<PXMediaGroup> getMediaGroups() { return mediaGroups != null ? new ArrayList<PXMediaGroup>(mediaGroups) : null; } public static PXStylesheet getCurrentApplicationStylesheet() { return currentApplicationStylesheet; } public static PXStylesheet getCurrentUserStylesheet() { return currentUserStylesheet; } public static PXStylesheet getCurrentViewStylesheet() { return currentViewStylesheet; } // Setters public void setActiveMediaQuery(PXMediaExpression mediaExpression) { // TODO: test for equivalence of active query? If they match, then do // nothing this.activeMediaQuery = mediaExpression; this.activeMediaGroup = null; } // Methods public void addRuleSet(PXRuleSet ruleSet) { if (ruleSet != null) { if (activeMediaGroup == null) { activeMediaGroup = new PXMediaGroup(activeMediaQuery, origin); addMediaGroup(activeMediaGroup); } activeMediaGroup.addRuleSet(ruleSet); } } public void addMediaGroup(PXMediaGroup mediaGroup) { if (mediaGroups == null) { mediaGroups = new ArrayList<PXMediaGroup>(); } mediaGroups.add(mediaGroup); } public List<PXRuleSet> getRuleSetsMatchingStyleable(Object styleable) { List<PXRuleSet> result = new ArrayList<PXRuleSet>(); if (styleable != null) { List<PXRuleSet> candidateRuleSets = getRuleSets(styleable, PixateFreestyle.getAppContext()); if (candidateRuleSets != null) { for (PXRuleSet ruleSet : candidateRuleSets) { if (ruleSet.matches(styleable)) { if (PXLog.isLogging()) { PXLog.i(PXStylesheet.class.getSimpleName(), "%s matched\n%s", PXStyleUtils.getDescriptionForStyleable(styleable), ruleSet.toString()); } result.add(ruleSet); } } } } return result; } public void setURI(String uri, String prefix) { if (uri != null) { if (prefix == null) { prefix = StringUtil.EMPTY; } if (namespacePrefixMap == null) { namespacePrefixMap = new HashMap<String, String>(); } namespacePrefixMap.put(prefix, uri); } } public String getNamespaceForPrefix(String prefix) { if (namespacePrefixMap != null) { if (prefix == null) { prefix = StringUtil.EMPTY; } return namespacePrefixMap.get(prefix); } return null; } public void addKeyframe(PXKeyframe keyframe) { if (keyframe != null) { if (keyframesByName == null) { keyframesByName = new HashMap<String, PXKeyframe>(); } keyframesByName.put(keyframe.getName(), keyframe); } } public PXKeyframe getKeyframeForName(String name) { return keyframesByName == null ? null : keyframesByName.get(name); } public PXStyleSheetOrigin getOrigin() { return origin; } public PXMediaExpression getActiveMediaQuery() { return activeMediaQuery; } /** * Set the errors that were detected during the parsing of this stylesheet. * * @param errors */ public void setErrors(List<String> errors) { this.errors = errors; } /** * Returns the parsing errors. Can be <code>null</code>. * * @return A list of parsing error strings, or <code>null</code>. */ public List<String> getErrors() { return errors; } public void setFilePath(String filePath) { this.filePath = filePath; } public String getFilePath() { return filePath; } // Static private methods private static void assignCurrentStylesheet(PXStylesheet stylesheet, PXStyleSheetOrigin forOrigin) { switch (forOrigin) { case APPLICATION: currentApplicationStylesheet = stylesheet; break; case USER: currentUserStylesheet = stylesheet; break; case VIEW: currentViewStylesheet = stylesheet; break; case INLINE: // this origin type should never be handled here, but in // PXStyleController directly break; } } /* * (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { StringBuilder builder = new StringBuilder(); if (mediaGroups != null) { int size = mediaGroups.size(); for (int i = 0; i < size; i++) { builder.append(mediaGroups.get(i).toString()); if (i + 1 < size) { builder.append('\n'); } } } return builder.toString(); } }