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 
getCachedTemplate(Context context, int id)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 {
write(OutputStream stream, EntityData params)52         void write(OutputStream stream, EntityData params) throws IOException;
53     }
54 
55     interface EntityData {
writeValue(OutputStream stream, String key)56         void writeValue(OutputStream stream, String key) throws IOException;
getListIterator(String key)57         ListEntityIterator getListIterator(String key);
58     }
59 
60     interface ListEntityIterator extends EntityData {
reset()61         void reset();
moveToNext()62         boolean moveToNext();
63     }
64 
65     static class StringEntity implements Entity {
66 
67         byte[] mValue;
68 
StringEntity(String value)69         public StringEntity(String value) {
70             mValue = value.getBytes();
71         }
72 
73         @Override
write(OutputStream stream, EntityData params)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 
SimpleEntity(String key)84         public SimpleEntity(String key) {
85             mKey = key;
86         }
87 
88         @Override
write(OutputStream stream, EntityData params)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 
ListEntity(Context context, String key, String subTemplate)100         public ListEntity(Context context, String key, String subTemplate) {
101             mKey = key;
102             mSubTemplate = new Template(context, subTemplate);
103         }
104 
105         @Override
write(OutputStream stream, EntityData params)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 
CursorListEntityWrapper(Cursor cursor)120         public CursorListEntityWrapper(Cursor cursor) {
121             mCursor = cursor;
122         }
123 
124         @Override
moveToNext()125         public boolean moveToNext() {
126             return mCursor.moveToNext();
127         }
128 
129         @Override
reset()130         public void reset() {
131             mCursor.moveToPosition(-1);
132         }
133 
134         @Override
getListIterator(String key)135         public ListEntityIterator getListIterator(String key) {
136             return null;
137         }
138 
getCursor()139         public Cursor getCursor() {
140             return mCursor;
141         }
142 
143     }
144 
145     static class HashMapEntityData implements EntityData {
146 
147         HashMap<String, Object> mData;
148 
HashMapEntityData(HashMap<String, Object> map)149         public HashMapEntityData(HashMap<String, Object> map) {
150             mData = map;
151         }
152 
153         @Override
getListIterator(String key)154         public ListEntityIterator getListIterator(String key) {
155             return (ListEntityIterator) mData.get(key);
156         }
157 
158         @Override
writeValue(OutputStream stream, String key)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>();
Template(Context context, int tid)167     private Template(Context context, int tid) {
168         this(context, readRaw(context, tid));
169     }
170 
Template(Context context, String template)171     private Template(Context context, String template) {
172         mTemplate = new ArrayList<Entity>();
173         template = replaceConsts(context, template);
174         parseTemplate(context, template);
175     }
176 
Template(Template copy)177     private Template(Template copy) {
178         mTemplate = copy.mTemplate;
179     }
180 
copy()181     Template copy() {
182         return new Template(this);
183     }
184 
parseTemplate(Context context, String template)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 
assign(String name, String value)218     public void assign(String name, String value) {
219         mData.put(name, value.getBytes());
220     }
221 
assignLoop(String name, ListEntityIterator iter)222     public void assignLoop(String name, ListEntityIterator iter) {
223         mData.put(name, iter);
224     }
225 
write(OutputStream stream)226     public void write(OutputStream stream) throws IOException {
227         write(stream, new HashMapEntityData(mData));
228     }
229 
write(OutputStream stream, EntityData data)230     public void write(OutputStream stream, EntityData data) throws IOException {
231         for (Entity ent : mTemplate) {
232             ent.write(stream, data);
233         }
234     }
235 
replaceConsts(Context context, String template)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 
readRaw(Context context, int id)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