/* * Copyright (C) 2011 The Android Open Source Project * * 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.android.browser.homepages; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import android.content.Context; import android.content.res.Resources; import android.database.Cursor; import android.util.TypedValue; import com.android.browser.R; public class Template { private static HashMap<Integer, Template> sCachedTemplates = new HashMap<Integer, Template>(); public static Template getCachedTemplate(Context context, int id) { synchronized (sCachedTemplates) { Template template = sCachedTemplates.get(id); if (template == null) { template = new Template(context, id); sCachedTemplates.put(id, template); } // Return a copy so that we don't share data return template.copy(); } } interface Entity { void write(OutputStream stream, EntityData params) throws IOException; } interface EntityData { void writeValue(OutputStream stream, String key) throws IOException; ListEntityIterator getListIterator(String key); } interface ListEntityIterator extends EntityData { void reset(); boolean moveToNext(); } static class StringEntity implements Entity { byte[] mValue; public StringEntity(String value) { mValue = value.getBytes(); } @Override public void write(OutputStream stream, EntityData params) throws IOException { stream.write(mValue); } } static class SimpleEntity implements Entity { String mKey; public SimpleEntity(String key) { mKey = key; } @Override public void write(OutputStream stream, EntityData params) throws IOException { params.writeValue(stream, mKey); } } static class ListEntity implements Entity { String mKey; Template mSubTemplate; public ListEntity(Context context, String key, String subTemplate) { mKey = key; mSubTemplate = new Template(context, subTemplate); } @Override public void write(OutputStream stream, EntityData params) throws IOException { ListEntityIterator iter = params.getListIterator(mKey); iter.reset(); while (iter.moveToNext()) { mSubTemplate.write(stream, iter); } } } public abstract static class CursorListEntityWrapper implements ListEntityIterator { private Cursor mCursor; public CursorListEntityWrapper(Cursor cursor) { mCursor = cursor; } @Override public boolean moveToNext() { return mCursor.moveToNext(); } @Override public void reset() { mCursor.moveToPosition(-1); } @Override public ListEntityIterator getListIterator(String key) { return null; } public Cursor getCursor() { return mCursor; } } static class HashMapEntityData implements EntityData { HashMap<String, Object> mData; public HashMapEntityData(HashMap<String, Object> map) { mData = map; } @Override public ListEntityIterator getListIterator(String key) { return (ListEntityIterator) mData.get(key); } @Override public void writeValue(OutputStream stream, String key) throws IOException { stream.write((byte[]) mData.get(key)); } } private List<Entity> mTemplate; private HashMap<String, Object> mData = new HashMap<String, Object>(); private Template(Context context, int tid) { this(context, readRaw(context, tid)); } private Template(Context context, String template) { mTemplate = new ArrayList<Entity>(); template = replaceConsts(context, template); parseTemplate(context, template); } private Template(Template copy) { mTemplate = copy.mTemplate; } Template copy() { return new Template(this); } void parseTemplate(Context context, String template) { final Pattern pattern = Pattern.compile("<%([=\\{])\\s*(\\w+)\\s*%>"); Matcher m = pattern.matcher(template); int start = 0; while (m.find()) { String static_part = template.substring(start, m.start()); if (static_part.length() > 0) { mTemplate.add(new StringEntity(static_part)); } String type = m.group(1); String name = m.group(2); if (type.equals("=")) { mTemplate.add(new SimpleEntity(name)); } else if (type.equals("{")) { Pattern p = Pattern.compile("<%\\}\\s*" + Pattern.quote(name) + "\\s*%>"); Matcher end_m = p.matcher(template); if (end_m.find(m.end())) { start = m.end(); m.region(end_m.end(), template.length()); String subTemplate = template.substring(start, end_m.start()); mTemplate.add(new ListEntity(context, name, subTemplate)); start = end_m.end(); continue; } } start = m.end(); } String static_part = template.substring(start, template.length()); if (static_part.length() > 0) { mTemplate.add(new StringEntity(static_part)); } } public void assign(String name, String value) { mData.put(name, value.getBytes()); } public void assignLoop(String name, ListEntityIterator iter) { mData.put(name, iter); } public void write(OutputStream stream) throws IOException { write(stream, new HashMapEntityData(mData)); } public void write(OutputStream stream, EntityData data) throws IOException { for (Entity ent : mTemplate) { ent.write(stream, data); } } private static String replaceConsts(Context context, String template) { final Pattern pattern = Pattern.compile("<%@\\s*(\\w+/\\w+)\\s*%>"); final Resources res = context.getResources(); final String packageName = R.class.getPackage().getName(); Matcher m = pattern.matcher(template); StringBuffer sb = new StringBuffer(); while (m.find()) { String name = m.group(1); if (name.startsWith("drawable/")) { m.appendReplacement(sb, "res/" + name); } else { int id = res.getIdentifier(name, null, packageName); if (id != 0) { TypedValue value = new TypedValue(); res.getValue(id, value, true); String replacement; if (value.type == TypedValue.TYPE_DIMENSION) { float dimen = res.getDimension(id); int dimeni = (int) dimen; if (dimeni == dimen) replacement = Integer.toString(dimeni); else replacement = Float.toString(dimen); } else { replacement = value.coerceToString().toString(); } m.appendReplacement(sb, replacement); } } } m.appendTail(sb); return sb.toString(); } private static String readRaw(Context context, int id) { InputStream ins = context.getResources().openRawResource(id); try { byte[] buf = new byte[ins.available()]; ins.read(buf); return new String(buf, "utf-8"); } catch (IOException ex) { return "<html><body>Error</body></html>"; } } }