package com.dianping.ant; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.security.MessageDigest; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Map.Entry; import java.util.Properties; import java.util.regex.Pattern; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; import org.json.JSONArray; import org.json.JSONObject; public class GenSite extends Task { private File src = null; private String name = null; /** * the project dir */ public void setSrc(File f) { src = f; } /** * the project name, like dev.home */ public void setName(String str) { name = str; } @Override public void execute() throws BuildException { if (src == null) { throw new BuildException("src is missing"); } if (!src.isDirectory()) { throw new BuildException(src + " is not a directory"); } if (name == null || name.length() == 0) { throw new BuildException("name is missing"); } File myApk = new File(src, "bin"); myApk = new File(myApk, name + ".apk"); if (myApk.length() == 0) { System.out.println(myApk + " is missing"); File bin = new File(src, "bin"); File site = new File(bin, "site.txt"); site.delete(); return; } final ArrayList<File> directLibrary = new ArrayList<File>(); File propProj = new File(src, "project.properties"); if (propProj.length() > 0) { Properties p = new Properties(); try { FileInputStream fis = new FileInputStream(propProj); p.load(fis); fis.close(); } catch (Exception e) { } int f = 0, i = 0; while (f < 10) { Object v = p.get("android.library.reference." + (++i)); if (v == null) { f++; continue; } File dir = new File(src, String.valueOf(v)); directLibrary.add(dir); } } final ArrayList<JSONObject> sites = new ArrayList<JSONObject>(); for (File lib : directLibrary) { File bin = new File(lib, "bin"); File site = new File(bin, "site.txt"); if (site.length() == 0) { throw new BuildException("site.txt is missing under " + bin); } JSONObject json; try { FileInputStream fis = new FileInputStream(site); byte[] bytes = new byte[fis.available()]; fis.read(bytes); fis.close(); String str = new String(bytes, "ASCII"); json = new JSONObject(str); } catch (Exception e) { throw new BuildException("unable to read " + site); } sites.add(json); } try { JSONObject json = new JSONObject(); SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMdd"); String today = fmt.format(new Date()); json.put("id", name + "." + today + ".0"); json.put("version", today + ".0"); System.out.println("site.txt id=" + name + "." + today + ".0"); // // files // last is myself // JSONArray files = new JSONArray(); final HashMap<String, JSONObject> fileMap = new HashMap<String, JSONObject>(); for (int i = 0; i < directLibrary.size(); i++) { File dir = directLibrary.get(i); JSONObject site = sites.get(i); JSONArray siteFiles = site.getJSONArray("files"); for (int j = 0; j < siteFiles.length(); j++) { JSONObject f = siteFiles.getJSONObject(j); JSONObject exists = fileMap.get(f.getString("id")); if (exists != null) { if (!exists.getString("md5").equals(f.get("md5"))) { throw new BuildException("md5 conflict on " + f.getString("id") + "\n" + dir); } } else { String url = f.getString("url"); if (url.startsWith("file://")) { File apk = new File(dir, url.substring("file://" .length())); File rel = getRelativeFile(apk, src); f.put("url", "file://" + rel); } files.put(f); fileMap.put(f.getString("id"), f); } } } // last is myself String myFileId = name + "." + today + ".0"; JSONObject myFile = new JSONObject(); myFile.put("id", myFileId); myFile.put("url", "file://bin/" + name + ".apk"); myFile.put("md5", md5(myApk)); if (directLibrary.size() > 0) { JSONArray deps = new JSONArray(); for (JSONObject site : sites) { JSONArray siteFiles = site.getJSONArray("files"); String str = siteFiles .getJSONObject(siteFiles.length() - 1).getString( "id"); deps.put(str); } myFile.put("deps", deps); } files.put(myFile); json.put("files", files); // // fragments // ArrayList<JSONObject> fragments = new ArrayList<JSONObject>(); HashMap<String, JSONObject> fragmentMap = new HashMap<String, JSONObject>(); for (JSONObject site : sites) { JSONArray arr = site.getJSONArray("fragments"); for (int i = 0; i < arr.length(); i++) { JSONObject fragment = arr.getJSONObject(i); String host = fragment.getString("host"); if (fragmentMap.containsKey(host)) { System.out.println("dianping://" + host + " is override by " + site.get("id")); fragments.remove(fragmentMap.get(host)); } fragments.add(fragment); fragmentMap.put(host, fragment); } } // my fragments Properties ps = new Properties(); { File f = new File(src, "fragment.properties"); if (f.length() == 0) { System.out.println("fragment.properties is missing"); } else { try { FileInputStream fis = new FileInputStream(f); ps.load(fis); fis.close(); } catch (Exception e) { throw new BuildException("fail to load " + f, e); } } } for (Entry<Object, Object> e : ps.entrySet()) { String key = String.valueOf(e.getKey()).trim(); String val = String.valueOf(e.getValue()).trim(); if (!"default".equals(key)) { if (fragmentMap.containsKey(key)) { System.out.println("dianping://" + key + " is override by " + myFileId); fragments.remove(fragmentMap.get(key)); } JSONObject fragment = new JSONObject(); fragment.put("host", key); fragment.put("code", myFileId); fragment.put("name", val); fragments.add(fragment); } } JSONArray arr = new JSONArray(); for (JSONObject fragment : fragments) { arr.put(fragment); } json.put("fragments", arr); File output = new File(src, "bin"); output = new File(output, "site.txt"); FileOutputStream fos = new FileOutputStream(output); fos.write(json.toString(2).getBytes("ASCII")); fos.close(); System.out.println(output); } catch (Exception e) { throw new BuildException("unable to build site.txt"); } } private static String md5(File file) throws Exception { FileInputStream fis = new FileInputStream(file); byte[] bytes = new byte[fis.available()]; fis.read(bytes); fis.close(); MessageDigest md = MessageDigest.getInstance("MD5"); byte[] md5b = md.digest(bytes); String md5 = byteArrayToHexString(md5b); return md5; } private final static String[] hexDigits = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; public static String byteArrayToHexString(byte[] b) { StringBuilder resultSb = new StringBuilder(); for (int i = 0; i < b.length; i++) { resultSb.append(byteToHexString(b[i])); } return resultSb.toString(); } private static String byteToHexString(byte b) { int n = b; if (n < 0) n = 0x100 + n; int d1 = n >> 4; int d2 = n & 0xF; return hexDigits[d1] + hexDigits[d2]; } /** * Returns the path of one File relative to another. * * @param target * the target directory * @param base * the base directory * @return target's path relative to the base directory * @throws IOException * if an error occurs while resolving the files' canonical names */ public static File getRelativeFile(File target, File base) throws IOException { String[] baseComponents = base.getCanonicalPath().split( Pattern.quote(File.separator)); String[] targetComponents = target.getCanonicalPath().split( Pattern.quote(File.separator)); // skip common components int index = 0; for (; index < targetComponents.length && index < baseComponents.length; ++index) { if (!targetComponents[index].equals(baseComponents[index])) break; } StringBuilder result = new StringBuilder(); if (index != baseComponents.length) { // backtrack to base directory for (int i = index; i < baseComponents.length; ++i) result.append(".." + File.separator); } for (; index < targetComponents.length; ++index) result.append(targetComponents[index] + File.separator); if (!target.getPath().endsWith("/") && !target.getPath().endsWith("\\")) { // remove final path separator result.delete(result.length() - File.separator.length(), result.length()); } return new File(result.toString()); } }