1 2 /* 3 * Copyright (C) 2011 The Android Open Source Project 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package com.android.browser.homepages; 18 19 import java.io.IOException; 20 import java.io.InputStream; 21 import java.io.OutputStream; 22 import java.util.ArrayList; 23 import java.util.HashMap; 24 import java.util.List; 25 import java.util.regex.Matcher; 26 import java.util.regex.Pattern; 27 28 import android.content.Context; 29 import android.content.res.Resources; 30 import android.database.Cursor; 31 import android.util.TypedValue; 32 33 import com.android.browser.R; 34 35 public class Template { 36 37 private static HashMap<Integer, Template> sCachedTemplates = new HashMap<Integer, Template>(); 38 39 public static Template getCachedTemplate(Context context, int id) { 40 synchronized (sCachedTemplates) { 41 Template template = sCachedTemplates.get(id); 42 if (template == null) { 43 template = new Template(context, id); 44 sCachedTemplates.put(id, template); 45 } 46 // Return a copy so that we don't share data 47 return template.copy(); 48 } 49 } 50 51 interface Entity { 52 void write(OutputStream stream, EntityData params) throws IOException; 53 } 54 55 interface EntityData { 56 void writeValue(OutputStream stream, String key) throws IOException; 57 ListEntityIterator getListIterator(String key); 58 } 59 60 interface ListEntityIterator extends EntityData { 61 void reset(); 62 boolean moveToNext(); 63 } 64 65 static class StringEntity implements Entity { 66 67 byte[] mValue; 68 69 public StringEntity(String value) { 70 mValue = value.getBytes(); 71 } 72 73 @Override 74 public void write(OutputStream stream, EntityData params) throws IOException { 75 stream.write(mValue); 76 } 77 78 } 79 80 static class SimpleEntity implements Entity { 81 82 String mKey; 83 84 public SimpleEntity(String key) { 85 mKey = key; 86 } 87 88 @Override 89 public void write(OutputStream stream, EntityData params) throws IOException { 90 params.writeValue(stream, mKey); 91 } 92 93 } 94 95 static class ListEntity implements Entity { 96 97 String mKey; 98 Template mSubTemplate; 99 100 public ListEntity(Context context, String key, String subTemplate) { 101 mKey = key; 102 mSubTemplate = new Template(context, subTemplate); 103 } 104 105 @Override 106 public void write(OutputStream stream, EntityData params) throws IOException { 107 ListEntityIterator iter = params.getListIterator(mKey); 108 iter.reset(); 109 while (iter.moveToNext()) { 110 mSubTemplate.write(stream, iter); 111 } 112 } 113 114 } 115 116 public abstract static class CursorListEntityWrapper implements ListEntityIterator { 117 118 private Cursor mCursor; 119 120 public CursorListEntityWrapper(Cursor cursor) { 121 mCursor = cursor; 122 } 123 124 @Override 125 public boolean moveToNext() { 126 return mCursor.moveToNext(); 127 } 128 129 @Override 130 public void reset() { 131 mCursor.moveToPosition(-1); 132 } 133 134 @Override 135 public ListEntityIterator getListIterator(String key) { 136 return null; 137 } 138 139 public Cursor getCursor() { 140 return mCursor; 141 } 142 143 } 144 145 static class HashMapEntityData implements EntityData { 146 147 HashMap<String, Object> mData; 148 149 public HashMapEntityData(HashMap<String, Object> map) { 150 mData = map; 151 } 152 153 @Override 154 public ListEntityIterator getListIterator(String key) { 155 return (ListEntityIterator) mData.get(key); 156 } 157 158 @Override 159 public void writeValue(OutputStream stream, String key) throws IOException { 160 stream.write((byte[]) mData.get(key)); 161 } 162 163 } 164 165 private List<Entity> mTemplate; 166 private HashMap<String, Object> mData = new HashMap<String, Object>(); 167 private Template(Context context, int tid) { 168 this(context, readRaw(context, tid)); 169 } 170 171 private Template(Context context, String template) { 172 mTemplate = new ArrayList<Entity>(); 173 template = replaceConsts(context, template); 174 parseTemplate(context, template); 175 } 176 177 private Template(Template copy) { 178 mTemplate = copy.mTemplate; 179 } 180 181 Template copy() { 182 return new Template(this); 183 } 184 185 void parseTemplate(Context context, String template) { 186 final Pattern pattern = Pattern.compile("<%([=\\{])\\s*(\\w+)\\s*%>"); 187 Matcher m = pattern.matcher(template); 188 int start = 0; 189 while (m.find()) { 190 String static_part = template.substring(start, m.start()); 191 if (static_part.length() > 0) { 192 mTemplate.add(new StringEntity(static_part)); 193 } 194 String type = m.group(1); 195 String name = m.group(2); 196 if (type.equals("=")) { 197 mTemplate.add(new SimpleEntity(name)); 198 } else if (type.equals("{")) { 199 Pattern p = Pattern.compile("<%\\}\\s*" + Pattern.quote(name) + "\\s*%>"); 200 Matcher end_m = p.matcher(template); 201 if (end_m.find(m.end())) { 202 start = m.end(); 203 m.region(end_m.end(), template.length()); 204 String subTemplate = template.substring(start, end_m.start()); 205 mTemplate.add(new ListEntity(context, name, subTemplate)); 206 start = end_m.end(); 207 continue; 208 } 209 } 210 start = m.end(); 211 } 212 String static_part = template.substring(start, template.length()); 213 if (static_part.length() > 0) { 214 mTemplate.add(new StringEntity(static_part)); 215 } 216 } 217 218 public void assign(String name, String value) { 219 mData.put(name, value.getBytes()); 220 } 221 222 public void assignLoop(String name, ListEntityIterator iter) { 223 mData.put(name, iter); 224 } 225 226 public void write(OutputStream stream) throws IOException { 227 write(stream, new HashMapEntityData(mData)); 228 } 229 230 public void write(OutputStream stream, EntityData data) throws IOException { 231 for (Entity ent : mTemplate) { 232 ent.write(stream, data); 233 } 234 } 235 236 private static String replaceConsts(Context context, String template) { 237 final Pattern pattern = Pattern.compile("<%@\\s*(\\w+/\\w+)\\s*%>"); 238 final Resources res = context.getResources(); 239 final String packageName = R.class.getPackage().getName(); 240 Matcher m = pattern.matcher(template); 241 StringBuffer sb = new StringBuffer(); 242 while (m.find()) { 243 String name = m.group(1); 244 if (name.startsWith("drawable/")) { 245 m.appendReplacement(sb, "res/" + name); 246 } else { 247 int id = res.getIdentifier(name, null, packageName); 248 if (id != 0) { 249 TypedValue value = new TypedValue(); 250 res.getValue(id, value, true); 251 String replacement; 252 if (value.type == TypedValue.TYPE_DIMENSION) { 253 float dimen = res.getDimension(id); 254 int dimeni = (int) dimen; 255 if (dimeni == dimen) 256 replacement = Integer.toString(dimeni); 257 else 258 replacement = Float.toString(dimen); 259 } else { 260 replacement = value.coerceToString().toString(); 261 } 262 m.appendReplacement(sb, replacement); 263 } 264 } 265 } 266 m.appendTail(sb); 267 return sb.toString(); 268 } 269 270 private static String readRaw(Context context, int id) { 271 InputStream ins = context.getResources().openRawResource(id); 272 try { 273 byte[] buf = new byte[ins.available()]; 274 ins.read(buf); 275 return new String(buf, "utf-8"); 276 } catch (IOException ex) { 277 return "<html><body>Error</body></html>"; 278 } 279 } 280 281 } 282