/*
* 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>";
}
}
}