diff options
| author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-13 13:04:19 -0700 |
|---|---|---|
| committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-13 13:04:19 -0700 |
| commit | 59008ebc2c43def1ca918d8f6bcdb5e14df394ef (patch) | |
| tree | c485998f2f31b1126b405c5d3c88d8e2052d3768 /tools/makedict/src | |
| parent | 243d18eb22363fcfe5fd76d93c8d2e30f1246ffd (diff) | |
| download | android_development-59008ebc2c43def1ca918d8f6bcdb5e14df394ef.tar.gz android_development-59008ebc2c43def1ca918d8f6bcdb5e14df394ef.tar.bz2 android_development-59008ebc2c43def1ca918d8f6bcdb5e14df394ef.zip | |
auto import from //branches/cupcake_rel/...@138607
Diffstat (limited to 'tools/makedict/src')
| -rwxr-xr-x | tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java b/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java new file mode 100755 index 000000000..cbe702872 --- /dev/null +++ b/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2009 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.tools.dict; + +import org.xml.sax.Attributes; +import org.xml.sax.helpers.DefaultHandler; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +/** + * Compresses a list of words and frequencies into a tree structured binary dictionary. + */ +public class MakeBinaryDictionary { + + public static final int ALPHA_SIZE = 256; + + public static final String TAG_WORD = "w"; + public static final String ATTR_FREQ = "f"; + + public static final CharNode EMPTY_NODE = new CharNode(); + + List<CharNode> roots; + Map<String, Integer> mDictionary; + int mWordCount; + + static class CharNode { + char data; + int freq; + boolean terminal; + List<CharNode> children; + static int sNodes; + + public CharNode() { + sNodes++; + } + } + + public static void usage() { + System.err.println("Usage: makedict <src.xml> <dest.dict>"); + System.exit(-1); + } + + public static void main(String[] args) { + if (args.length < 2) { + usage(); + } else { + new MakeBinaryDictionary(args[0], args[1]); + } + } + + public MakeBinaryDictionary(String srcFilename, String destFilename) { + populateDictionary(srcFilename); + writeToDict(destFilename); + // Enable the code below to verify that the generated tree is traversable. + if (false) { + traverseDict(0, new char[32], 0); + } + } + + private void populateDictionary(String filename) { + roots = new ArrayList<CharNode>(); + try { + SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); + parser.parse(new File(filename), new DefaultHandler() { + boolean inWord; + int freq; + + @Override + public void startElement(String uri, String localName, + String qName, Attributes attributes) { + if (qName.equals("w")) { + inWord = true; + freq = Integer.parseInt(attributes.getValue(0)); + } + } + + @Override + public void characters(char[] data, int offset, int length) { + // Ignore other whitespace + if (!inWord) return; + + // Ignore one letter words + if (length < 2) return; + mWordCount++; + String word = new String(data, offset, length); + addWordTop(word, freq); + } + + @Override + public void endElement(String uri, String localName, + String qName) { + if (qName.equals("w")) inWord = false; + } + }); + } catch (Exception ioe) { + System.err.println("Exception in parsing\n" + ioe); + ioe.printStackTrace(); + } + System.out.println("Nodes = " + CharNode.sNodes); + } + + private int indexOf(List<CharNode> children, char c) { + if (children == null) { + return -1; + } + for (int i = 0; i < children.size(); i++) { + if (children.get(i).data == c) { + return i; + } + } + return -1; + } + + private void addWordTop(String word, int occur) { + if (occur > 255) occur = 255; + + char firstChar = word.charAt(0); + int index = indexOf(roots, firstChar); + if (index == -1) { + CharNode newNode = new CharNode(); + newNode.data = firstChar; + newNode.freq = occur; + index = roots.size(); + roots.add(newNode); + } else { + roots.get(index).freq += occur; + } + if (word.length() > 1) { + addWordRec(roots.get(index), word, 1, occur); + } else { + roots.get(index).terminal = true; + } + } + + private void addWordRec(CharNode parent, String word, int charAt, int occur) { + CharNode child = null; + char data = word.charAt(charAt); + if (parent.children == null) { + parent.children = new ArrayList<CharNode>(); + } else { + for (int i = 0; i < parent.children.size(); i++) { + CharNode node = parent.children.get(i); + if (node.data == data) { + child = node; + break; + } + } + } + if (child == null) { + child = new CharNode(); + parent.children.add(child); + } + child.data = data; + child.freq += occur; + if (word.length() > charAt + 1) { + addWordRec(child, word, charAt + 1, occur); + } else { + child.terminal = true; + child.freq = occur; + } + } + + byte[] dict; + int dictSize; + static final int CHAR_WIDTH = 8; + static final int FLAGS_WIDTH = 1; // Terminal flag (word end) + static final int ADDR_WIDTH = 23; // Offset to children + static final int FREQ_WIDTH_BYTES = 1; + static final int COUNT_WIDTH_BYTES = 1; + static final int NODE_SIZE_BYTES = + (CHAR_WIDTH + FLAGS_WIDTH + ADDR_WIDTH) / 8 + FREQ_WIDTH_BYTES; + + private void addCount(int count) { + dict[dictSize++] = (byte) (0xFF & count); + } + + /* TODO: Allow characters to be beyond the 0-255 range. This is required for some latin + language not currently supported */ + private void addNode(CharNode node) { + int charData = 0xFFFF & node.data; + if (charData > 254) { + System.out.println("WARNING: Non-ASCII character encountered : " + node.data + + ", value = " + charData); + dict[dictSize++] = '@'; + } else { + dict[dictSize++] = (byte) (0xFF & node.data); + } + dictSize += 3; // Space for children address + if ((0xFFFFFF & node.freq) > 255) { + node.freq = (byte) 255; + } + dict[dictSize++] = (byte) (0xFF & node.freq); + } + + private void updateNodeAddress(int nodeAddress, CharNode node, + int childrenAddress) { + childrenAddress = 0x7FFFFF & childrenAddress; + if (node.terminal) { + childrenAddress |= 0x800000; + } + dict[nodeAddress + 1] = (byte) (childrenAddress >> 16); + dict[nodeAddress + 2] = (byte) ((childrenAddress & 0xFF00) >> 8); + dict[nodeAddress + 3] = (byte) ((childrenAddress & 0xFF)); + } + + void writeWordsRec(List<CharNode> children) { + if (children == null || children.size() == 0) { + return; + } + addCount(children.size()); + int childrenStart = dictSize; + for (int j = 0; j < children.size(); j++) { + CharNode node = children.get(j); + addNode(node); + } + for (int j = 0; j < children.size(); j++) { + CharNode node = children.get(j); + // TODO: Fix this when child length becomes variable + int nodeAddress = childrenStart + NODE_SIZE_BYTES * j; + int cacheDictSize = dictSize; + writeWordsRec(node.children); + updateNodeAddress(nodeAddress, node, node.children != null + ? cacheDictSize : 0); + } + } + + void writeToDict(String dictFilename) { + // 2MB max + dict = new byte[2 * 1024 * 1024]; // 2MB upper limit. Actual is probably + // < 1MB in most cases, as there is a limit in the + // resource size in apks. + dictSize = 0; + writeWordsRec(roots); + System.out.println("Dict Size = " + dictSize); + try { + FileOutputStream fos = new FileOutputStream(dictFilename); + fos.write(dict, 0, dictSize); + fos.close(); + } catch (IOException ioe) { + System.err.println("Error writing dict file:" + ioe); + } + } + + void traverseDict(int pos, char[] word, int depth) { + int count = dict[pos++] & 0xFF; + for (int i = 0; i < count; i++) { + char c = (char) (dict[pos] & 0xFF); + word[depth] = c; + if ((dict[pos + 1] & 0x80) > 0) { + showWord(word, depth + 1, dict[pos + 4] & 0xFF); + } + int address = + ((dict[pos + 1] & 0x7F) << 16) + | ((dict[pos + 2] & 0xFF) << 8) + | ((dict[pos + 3] & 0xFF)); + if (address != 0) { + traverseDict(address, word, depth + 1); + } + pos += NODE_SIZE_BYTES; + } + } + + void showWord(char[] word, int size, int freq) { + System.out.print(new String(word, 0, size) + " " + freq + "\n"); + } +} |
