iterator() {
279 | return data.iterator();
280 | }
281 |
282 | /**
283 | * @see {@link java.util.ArrayList#size()}
284 | */
285 | public int size() {
286 | return data.size();
287 | }
288 |
289 | }
--------------------------------------------------------------------------------
/android-plist-parser-app/src/com/longevitysoft/android/xml/plist/PListXMLHandler.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Licensed under Creative Commons Attribution 3.0 Unported license.
3 | * http://creativecommons.org/licenses/by/3.0/
4 | * You are free to copy, distribute and transmit the work, and
5 | * to adapt the work. You must attribute android-plist-parser
6 | * to Free Beachler (http://www.freebeachler.com).
7 | *
8 | * The Android PList parser (android-plist-parser) is distributed in
9 | * the hope that it will be useful, but WITHOUT ANY WARRANTY; without
10 | * even the implied warranty of MERCHANTABILITY or FITNESS FOR A
11 | * PARTICULAR PURPOSE.
12 | */
13 | package com.longevitysoft.android.xml.plist;
14 |
15 | import org.xml.sax.Attributes;
16 | import org.xml.sax.SAXException;
17 | import org.xml.sax.ext.DefaultHandler2;
18 |
19 | import android.util.Log;
20 |
21 | import com.longevitysoft.android.util.Stringer;
22 | import com.longevitysoft.android.xml.plist.domain.PList;
23 | import com.longevitysoft.android.xml.plist.domain.PListObject;
24 |
25 | /**
26 | *
27 | * Parse the a PList. Documentation on PLists can be found at: The Mac OS X Reference
30 | *
31 | *
32 | *
33 | * @author fbeachler
34 | *
35 | */
36 | public class PListXMLHandler extends DefaultHandler2 {
37 |
38 | public static final java.lang.String TAG = "PListXMLHandler";
39 |
40 | /**
41 | * Defines the modes the parser reports to registered listeners.
42 | *
43 | * @author fbeachler
44 | *
45 | */
46 | public enum ParseMode {
47 | START_TAG, END_TAG
48 | };
49 |
50 | /**
51 | * Implementors can listen for events defined by {@link ParseMode}.
52 | *
53 | * @author fbeachler
54 | *
55 | */
56 | public static interface PListParserListener {
57 | public void onPListParseDone(PList pList, ParseMode mode);
58 | }
59 |
60 | /**
61 | * {@link Stringer} for this class.
62 | */
63 | private Stringer stringer;
64 |
65 | /**
66 | * Listener for this parser.
67 | */
68 | private PListParserListener parseListener;
69 |
70 | /**
71 | * The value of parsed characters from elements and attributes.
72 | */
73 | private Stringer tempVal;
74 |
75 | /**
76 | * The parsed {@link PList}.
77 | */
78 | private PList pList;
79 |
80 | // Registers to hold state of parsing the workflow as Dict
81 | protected java.lang.String key;
82 |
83 | /**
84 | *
85 | */
86 | public PListXMLHandler() {
87 | super();
88 | stringer = new Stringer();
89 | }
90 |
91 | /**
92 | * @return the pList
93 | */
94 | public PList getPlist() {
95 | return pList;
96 | }
97 |
98 | /**
99 | * @param pList
100 | * the pList to set
101 | */
102 | public void setPlist(PList plist) {
103 | this.pList = plist;
104 | }
105 |
106 | /**
107 | * @return the parseListener
108 | */
109 | public PListParserListener getParseListener() {
110 | return parseListener;
111 | }
112 |
113 | /**
114 | * @param parseListener
115 | * the parseListener to set
116 | */
117 | public void setParseListener(PListParserListener parseListener) {
118 | this.parseListener = parseListener;
119 | }
120 |
121 | /**
122 | * @return the tempVal
123 | */
124 | public Stringer getTempVal() {
125 | return tempVal;
126 | }
127 |
128 | /**
129 | * @param tempVal
130 | * the tempVal to set
131 | */
132 | public void setTempVal(Stringer tempVal) {
133 | this.tempVal = tempVal;
134 | }
135 |
136 | /*
137 | * (non-Javadoc)
138 | *
139 | * @see org.xml.sax.helpers.DefaultHandler#startDocument()
140 | */
141 | @Override
142 | public void startDocument() throws SAXException {
143 | super.startDocument();
144 | tempVal = new Stringer();
145 | pList = null;
146 | key = null;
147 | }
148 |
149 | /*
150 | * (non-Javadoc)
151 | *
152 | * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String,
153 | * java.lang.String, java.lang.String, org.xml.sax.Attributes)
154 | */
155 | @Override
156 | public void startElement(java.lang.String uri, java.lang.String localName,
157 | java.lang.String qName, Attributes attributes) throws SAXException {
158 | Log.v(stringer.newBuilder().append(TAG).append("#startElement")
159 | .toString(),
160 | stringer.newBuilder()
161 | .append("Start Element lname|uri|attr.length :")
162 | .append(localName).append(Constants.PIPE).append(uri)
163 | .append(Constants.PIPE).append(attributes.getLength())
164 | .toString());
165 | tempVal.newBuilder();
166 | if (localName.equalsIgnoreCase(Constants.TAG_PLIST)) {
167 | if (null != pList) {
168 | // there should only be one PList element in the root
169 | throw new SAXException(
170 | "there should only be one PList element in PList XML");
171 | }
172 | pList = new PList();
173 | } else {
174 | if (null == pList) {
175 | throw new SAXException(
176 | "invalid PList - please see http://www.apple.com/DTDs/PropertyList-1.0.dtd");
177 | }
178 | if (localName.equalsIgnoreCase(Constants.TAG_DICT) ||
179 | localName.equalsIgnoreCase(Constants.TAG_PLIST_ARRAY)) {
180 | try {
181 | PListObject objToAdd = pList.buildObject(localName, tempVal
182 | .getBuilder().toString());
183 | pList.stackObject(objToAdd, key);
184 | } catch (Exception e) {
185 | throw new SAXException(e);
186 | }
187 | }
188 | }
189 | }
190 |
191 | /*
192 | * (non-Javadoc)
193 | *
194 | * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
195 | */
196 | @Override
197 | public void characters(char[] ch, int start, int length)
198 | throws SAXException {
199 | Log.v(stringer.newBuilder().append(TAG).append("#characters")
200 | .toString(),
201 | stringer.newBuilder().append(ch).append(Constants.PIPE)
202 | .append(start).append(Constants.PIPE).append(length)
203 | .append(Constants.PIPE).toString());
204 | tempVal.getBuilder().append(new java.lang.String(ch, start, length));
205 | }
206 |
207 | /*
208 | * (non-Javadoc)
209 | *
210 | * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String,
211 | * java.lang.String, java.lang.String)
212 | */
213 | @Override
214 | public void endElement(java.lang.String uri, java.lang.String localName,
215 | java.lang.String qName) throws SAXException {
216 | Log.v(stringer.newBuilder().append(TAG).append("#endElement")
217 | .toString(),
218 | stringer.newBuilder().append("localName|qName|uri|tempVal: ")
219 | .append(localName).append(Constants.PIPE).append(qName)
220 | .append(Constants.PIPE).append(uri)
221 | .append(Constants.PIPE)
222 | .append(tempVal.getBuilder().toString()).toString());
223 | if (localName.equalsIgnoreCase(Constants.TAG_KEY)) {
224 | key = tempVal.getBuilder().toString().trim();
225 | } else if (localName.equalsIgnoreCase(Constants.TAG_DICT) ||
226 | localName.equalsIgnoreCase(Constants.TAG_PLIST_ARRAY)) {
227 | pList.popStack();
228 | } else if (!localName.equalsIgnoreCase(Constants.TAG_PLIST)) {
229 | try {
230 | PListObject objToAdd = pList.buildObject(localName, tempVal
231 | .getBuilder().toString());
232 | pList.stackObject(objToAdd, key);
233 | } catch (Exception e) {
234 | throw new SAXException(e);
235 | }
236 | key = null;
237 | } else if (localName.equalsIgnoreCase(Constants.TAG_PLIST)) {
238 | if (null != parseListener) {
239 | parseListener.onPListParseDone(pList, ParseMode.END_TAG);
240 | }
241 | }
242 | tempVal.newBuilder();
243 |
244 | }
245 |
246 | }
247 |
--------------------------------------------------------------------------------
/android-plist-parser-app/src/com/longevitysoft/android/xml/plist/domain/PList.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Licensed under Creative Commons Attribution 3.0 Unported license.
3 | * http://creativecommons.org/licenses/by/3.0/
4 | * You are free to copy, distribute and transmit the work, and
5 | * to adapt the work. You must attribute android-plist-parser
6 | * to Free Beachler (http://www.freebeachler.com).
7 | *
8 | * The Android PList parser (android-plist-parser) is distributed in
9 | * the hope that it will be useful, but WITHOUT ANY WARRANTY; without
10 | * even the implied warranty of MERCHANTABILITY or FITNESS FOR A
11 | * PARTICULAR PURPOSE.
12 | */
13 | package com.longevitysoft.android.xml.plist.domain;
14 |
15 | import java.util.Stack;
16 |
17 | import android.util.Log;
18 |
19 | import com.longevitysoft.android.util.Stringer;
20 | import com.longevitysoft.android.xml.plist.Constants;
21 |
22 | /**
23 | * A PList class contains the objects and methods used to build and access a
24 | * PList. TODO: refactor so this meets the contract stated above
25 | */
26 | public class PList {
27 |
28 | public static final java.lang.String TAG = "PList";
29 |
30 | private Stringer stringer;
31 |
32 | /**
33 | * The PList root config element.
34 | */
35 | private PListObject root;
36 |
37 | private boolean stackCtxInDict;
38 | private boolean stackCtxInArray;
39 | private int stackCtxNestedDepth;
40 | // TODO - replace with some type of Map
41 | private Stack stack;
42 |
43 | public PList() {
44 | stringer = new Stringer();
45 | stackCtxInDict = false;
46 | stackCtxInArray = false;
47 | stackCtxNestedDepth = 0;
48 | stack = new Stack();
49 | }
50 |
51 | /**
52 | * @return the PList root config element
53 | */
54 | public PListObject getRootElement() {
55 | return root;
56 | }
57 |
58 | /**
59 | * @param root
60 | * the PList root object to set
61 | */
62 | public void setRootElement(PListObject root) {
63 | this.root = root;
64 | }
65 |
66 | /**
67 | * @param pList
68 | * @param stackCtxNestedDepth
69 | * @param stackCtxInArray
70 | * @param stackCtxInDict
71 | * @param stack
72 | * @param obj
73 | * @param key
74 | */
75 | private void attachPListObjToParent(PListObject obj, java.lang.String key) {
76 | if (stackCtxInArray) {
77 | // attach obj to array parent
78 | attachPListObjToArrayParent(stack, obj);
79 | } else if (stackCtxInDict) {
80 | // attach obj to dict parent
81 | attachPListObjToDictParent(obj, key);
82 | } else if (stackCtxNestedDepth == 0) {
83 | // set root DICT elm
84 | setRootElement(obj);
85 | }
86 | }
87 |
88 | /**
89 | * @param stack
90 | * @param key
91 | * @param obj
92 | */
93 | private void attachPListObjToDictParent(PListObject obj,
94 | java.lang.String key) {
95 | Log.v(stringer.newBuilder().append(TAG)
96 | .append("#attachPListObjToDictParent").toString(),
97 | stringer.newBuilder().append("key|obj-type|obj: ").append(key)
98 | .append(Constants.PIPE).append(obj.getType())
99 | .append(Constants.PIPE).append(obj.toString())
100 | .append(Constants.PIPE).toString());
101 | Dict parent = (Dict) stack.pop();
102 | parent.putConfig(key, obj);
103 | stack.push(parent);
104 | }
105 |
106 | /**
107 | * @param stack
108 | * @param key
109 | * @param obj
110 | */
111 | private void attachPListObjToArrayParent(Stack stack,
112 | PListObject obj) {
113 | Log.v(stringer.newBuilder().append(TAG)
114 | .append("#attachPListObjToArrayParent").toString(),
115 | stringer.newBuilder().append("obj-type|obj: ")
116 | .append(Constants.PIPE).append(obj.getType())
117 | .append(Constants.PIPE).append(obj.toString())
118 | .append(Constants.PIPE).toString());
119 | Array parent = (Array) stack.pop();
120 | parent.add(obj);
121 | stack.push(parent);
122 | }
123 |
124 | /**
125 | * Stack an object onto {@link this}. Stacking means: sequentially adding
126 | * {@PListObject}s onto the {@PList}. The previous
127 | * object that was stacked affects the context of the current object being
128 | * stacked. For example - if the previous element stacked was an
129 | * {@link Array} or {@link Dict} - the current object being stacked will be
130 | * a child.
131 | *
132 | * @param obj
133 | * @param key
134 | * If the parent of the element being added is a {@link Dict} -
135 | * this is required and must be non-null. Otherwise it's not
136 | * used.
137 | * @throws Exception
138 | * TODO: refactor - move me
139 | */
140 | public void stackObject(PListObject obj, java.lang.String key)
141 | throws Exception {
142 | if (null == key && stackCtxInDict) {
143 | throw new Exception(
144 | "PList objects with Dict parents require a key.");
145 | }
146 | if (stackCtxNestedDepth > 0 && !stackCtxInDict && !stackCtxInArray) {
147 | // if obj is not at root, its parent should be an Array or
148 | // Dict
149 | throw new Exception(
150 | "PList elements that are not at the root should have an Array or Dict parent.");
151 | }
152 | switch (obj.getType()) {
153 | case DICT:
154 | attachPListObjToParent(obj, key);
155 | stack.push(obj);
156 | stackCtxInArray = false;
157 | stackCtxInDict = true;
158 | stackCtxNestedDepth++;
159 | break;
160 | case ARRAY:
161 | attachPListObjToParent(obj, key);
162 | stack.push(obj);
163 | stackCtxInArray = true;
164 | stackCtxInDict = false;
165 | stackCtxNestedDepth++;
166 | break;
167 | default:
168 | attachPListObjToParent(obj, key);
169 | }
170 | }
171 |
172 | /**
173 | * @param obj
174 | * @param key
175 | *
176 | * @todo refactor - move me - generating PLists from a stack of objets is
177 | * not part of being a PList.
178 | */
179 | public PListObject popStack() {
180 | if (stack.isEmpty()) {
181 | return null;
182 | }
183 | PListObject ret = stack.pop();
184 | stackCtxNestedDepth--;
185 | if (!stack.isEmpty()) {
186 | switch (stack.lastElement().getType()) {
187 | case DICT:
188 | stackCtxInArray = false;
189 | stackCtxInDict = true;
190 | break;
191 | case ARRAY:
192 | stackCtxInArray = true;
193 | stackCtxInDict = false;
194 | break;
195 | }
196 | } else {
197 | stackCtxInArray = false;
198 | stackCtxInDict = false;
199 | }
200 | return ret;
201 | }
202 |
203 | /**
204 | * Build a {@PListObject} from a string that matches one of
205 | * the tags defined in {@link Constants}.
206 | *
207 | * @param tag
208 | * @param value
209 | * can be null if tag equals {@link Constants#TAG_BOOL_FALSE} or
210 | * {@link Constants#TAG_BOOL_TRUE}.
211 | * @throws Exception
212 | *
213 | * @todo replace with factory for PListObject
214 | */
215 | public PListObject buildObject(java.lang.String tag, java.lang.String value)
216 | throws Exception {
217 | if (null == tag) {
218 | throw new Exception(
219 | "Cannot add a child with a null tag to a PList.");
220 | }
221 | PListObject ret = null;
222 | if (tag.equalsIgnoreCase(Constants.TAG_INTEGER)) {
223 | ret = new Integer();
224 | ((Integer) ret).setValue(value);
225 | } else if (tag.equalsIgnoreCase(Constants.TAG_STRING)) {
226 | ret = new String();
227 | ((String) ret).setValue(value);
228 | } else if (tag.equalsIgnoreCase(Constants.TAG_REAL)) {
229 | ret = new Real();
230 | ((Real) ret).setValue(value);
231 | } else if (tag.equalsIgnoreCase(Constants.TAG_DATE)) {
232 | ret = new Date();
233 | ((Date) ret).setValue(value);
234 | } else if (tag.equalsIgnoreCase(Constants.TAG_BOOL_FALSE)) {
235 | ret = new False();
236 | } else if (tag.equalsIgnoreCase(Constants.TAG_BOOL_TRUE)) {
237 | ret = new True();
238 | } else if (tag.equalsIgnoreCase(Constants.TAG_DATA)) {
239 | ret = new Data();
240 | ((Data) ret).setValue(value.trim(), true);
241 | } else if (tag.equalsIgnoreCase(Constants.TAG_DICT)) {
242 | ret = new Dict();
243 | } else if (tag.equalsIgnoreCase(Constants.TAG_PLIST_ARRAY)) {
244 | ret = new Array();
245 | }
246 | return ret;
247 | }
248 |
249 | /*
250 | * (non-Javadoc)
251 | *
252 | * @see java.lang.Object#toString()
253 | */
254 | @Override
255 | public java.lang.String toString() {
256 | if (null == root) {
257 | return null;
258 | }
259 | return root.toString();
260 | }
261 |
262 | }
--------------------------------------------------------------------------------
/android-plist-parser-app/.settings/org.eclipse.jdt.ui.prefs:
--------------------------------------------------------------------------------
1 | #Tue Dec 21 04:12:52 MST 2010
2 | eclipse.preferences.version=1
3 | org.eclipse.jdt.ui.javadoc=false
4 | org.eclipse.jdt.ui.text.custom_code_templates=/**\n * @return the ${bare_field_name}\n *//**\n * @param ${param} the ${bare_field_name} to set\n *//**\n * ${tags}\n *//**\n * Licensed under Creative Commons Attribution 3.0 Unported license.\n * http\://creativecommons.org/licenses/by/3.0/\n * You are free to copy, distribute and transmit the work, and \n * to adapt the work. You must attribute android-plist-parser \n * to Free Beachler (http\://www.freebeachler.com).\n * \n * The Android PList parser (android-plist-parser) is distributed in \n * the hope that it will be useful, but WITHOUT ANY WARRANTY; without \n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A \n * PARTICULAR PURPOSE.\n */\n/**\n * @author ${user}\n *\n * ${tags}\n *//**\n * \n *//**\n * ${tags}\n *//* (non-Javadoc)\n * ${see_to_overridden}\n *//**\n * ${tags}\n * ${see_to_target}\n */${filecomment}\n${package_declaration}\n\n${typecomment}\n${type_declaration}\n\n\n\n// ${todo} Auto-generated catch block\n${exception_var}.printStackTrace();// ${todo} Auto-generated method stub\n${body_statement}${body_statement}\n// ${todo} Auto-generated constructor stubreturn ${field};${field} \= ${param};/**\n * @return the ${bare_field_name}\n *//**\n * @param ${param} the ${bare_field_name} to set\n *//**\n * ${tags}\n *//**\n * \n *//**\n * @author ${user}\n *\n * ${tags}\n *//**\n * \n *//**\n * ${tags}\n *//* (non-Jsdoc)\n * ${see_to_overridden}\n *//**\n * ${tags}\n * ${see_to_target}\n */${filecomment}\n${package_declaration}\n\n${typecomment}\n${type_declaration}\n// ${todo} Auto-generated catch block\n${exception_var}.printStackTrace();// ${todo} Auto-generated function stub\n${body_statement}${body_statement}\n// ${todo} Auto-generated constructor stubreturn ${field};${field} \= ${param};
5 |
--------------------------------------------------------------------------------
/android-plist-parser-test/.settings/org.eclipse.jdt.ui.prefs:
--------------------------------------------------------------------------------
1 | #Tue Dec 21 04:13:14 MST 2010
2 | eclipse.preferences.version=1
3 | org.eclipse.jdt.ui.javadoc=false
4 | org.eclipse.jdt.ui.text.custom_code_templates=/**\n * @return the ${bare_field_name}\n *//**\n * @param ${param} the ${bare_field_name} to set\n *//**\n * ${tags}\n *//**\n * Licensed under Creative Commons Attribution 3.0 Unported license.\n * http\://creativecommons.org/licenses/by/3.0/\n * You are free to copy, distribute and transmit the work, and \n * to adapt the work. You must attribute android-plist-parser \n * to Free Beachler (http\://www.freebeachler.com).\n * \n * The Android PList parser (android-plist-parser) is distributed in \n * the hope that it will be useful, but WITHOUT ANY WARRANTY; without \n * even the implied warranty of MERCHANTABILITY or FITNESS FOR A \n * PARTICULAR PURPOSE.\n */\n/**\n * @author ${user}\n *\n * ${tags}\n *//**\n * \n *//**\n * ${tags}\n *//* (non-Javadoc)\n * ${see_to_overridden}\n *//**\n * ${tags}\n * ${see_to_target}\n */${filecomment}\n${package_declaration}\n\n${typecomment}\n${type_declaration}\n\n\n\n// ${todo} Auto-generated catch block\n${exception_var}.printStackTrace();// ${todo} Auto-generated method stub\n${body_statement}${body_statement}\n// ${todo} Auto-generated constructor stubreturn ${field};${field} \= ${param};/**\n * @return the ${bare_field_name}\n *//**\n * @param ${param} the ${bare_field_name} to set\n *//**\n * ${tags}\n *//**\n * \n *//**\n * @author ${user}\n *\n * ${tags}\n *//**\n * \n *//**\n * ${tags}\n *//* (non-Jsdoc)\n * ${see_to_overridden}\n *//**\n * ${tags}\n * ${see_to_target}\n */${filecomment}\n${package_declaration}\n\n${typecomment}\n${type_declaration}\n// ${todo} Auto-generated catch block\n${exception_var}.printStackTrace();// ${todo} Auto-generated function stub\n${body_statement}${body_statement}\n// ${todo} Auto-generated constructor stubreturn ${field};${field} \= ${param};
5 |
--------------------------------------------------------------------------------
/android-plist-parser-test/src/com/longevitysoft/android/test/plist/xml/PListXMLHandlerTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Licensed under Creative Commons Attribution 3.0 Unported license.
3 | * http://creativecommons.org/licenses/by/3.0/
4 | * You are free to copy, distribute and transmit the work, and
5 | * to adapt the work. You must attribute android-plist-parser
6 | * to Free Beachler (http://www.freebeachler.com).
7 | *
8 | * The Android PList parser (android-plist-parser) is distributed in
9 | * the hope that it will be useful, but WITHOUT ANY WARRANTY; without
10 | * even the implied warranty of MERCHANTABILITY or FITNESS FOR A
11 | * PARTICULAR PURPOSE.
12 | */
13 | package com.longevitysoft.android.test.plist.xml;
14 |
15 | import java.util.HashMap;
16 | import java.util.Map;
17 |
18 | import org.xml.sax.Attributes;
19 | import org.xml.sax.SAXException;
20 | import org.xml.sax.helpers.AttributesImpl;
21 |
22 | import android.test.AndroidTestCase;
23 |
24 | import com.longevitysoft.android.util.Stringer;
25 | import com.longevitysoft.android.xml.plist.PListXMLHandler;
26 | import com.longevitysoft.android.xml.plist.PListXMLHandler.PListParserListener;
27 | import com.longevitysoft.android.xml.plist.PListXMLHandler.ParseMode;
28 | import com.longevitysoft.android.xml.plist.domain.Array;
29 | import com.longevitysoft.android.xml.plist.domain.Dict;
30 | import com.longevitysoft.android.xml.plist.domain.Integer;
31 | import com.longevitysoft.android.xml.plist.domain.PList;
32 | import com.longevitysoft.android.xml.plist.domain.PListObject;
33 |
34 | /**
35 | * Tests individual methods in {@link PListXMLHandler}. Most of the coverage
36 | * achieved here can also be gotten by adding coverage to
37 | * {@link PListXMLParserTest}.
38 | *
39 | * @author fbeachler
40 | *
41 | */
42 | public class PListXMLHandlerTest extends AndroidTestCase {
43 |
44 | public static final String TAG_NAMESPACE = "foo";
45 | /**
46 | * The class under test.
47 | */
48 | protected PListXMLHandler handler;
49 | private boolean mockListenerInvoked;
50 |
51 | /*
52 | * (non-Javadoc)
53 | *
54 | * @see junit.framework.TestCase#setUp()
55 | */
56 | protected void setUp() throws Exception {
57 | super.setUp();
58 | handler = new PListXMLHandler();
59 | }
60 |
61 | /*
62 | * (non-Javadoc)
63 | *
64 | * @see junit.framework.TestCase#tearDown()
65 | */
66 | protected void tearDown() throws Exception {
67 | handler = null;
68 | super.tearDown();
69 | }
70 |
71 | public void testTempValGetterSetter() {
72 | assertNull(handler.getTempVal());
73 | Stringer expected = new Stringer();
74 | handler.setTempVal(expected);
75 | assertEquals(expected, handler.getTempVal());
76 | }
77 |
78 | public void testPListGetterSetter() {
79 | assertNull(handler.getPlist());
80 | PList expected = new PList();
81 | handler.setPlist(expected);
82 | assertEquals(expected, handler.getPlist());
83 | }
84 |
85 | /**
86 | * Test method for
87 | * {@link com.longevitysoft.android.xml.plist.PListXMLHandler#startDocument()}
88 | * .
89 | *
90 | * @throws SAXException
91 | */
92 | public void testStartDocument() throws SAXException {
93 | assertNull(handler.getPlist());
94 | handler.startDocument();
95 | assertNull(handler.getPlist());
96 | assertNull(handler.getParseListener());
97 | assertNotNull(handler.getTempVal());
98 | }
99 |
100 | /**
101 | * Test method for
102 | * {@link com.longevitysoft.android.xml.plist.PListXMLHandler#characters(char[], int, int)}
103 | * .
104 | *
105 | * @throws SAXException
106 | */
107 | public void testCharacters() throws SAXException {
108 | handler.setTempVal(new Stringer());
109 | char[] chars = { 'f', 'o', 'o', 'b', 'a', 'r' };
110 | handler.characters(chars, 1, 3);
111 | assertEquals("oob", handler.getTempVal().getBuilder().toString());
112 | }
113 |
114 | /**
115 | * Test method for
116 | * {@link com.longevitysoft.android.xml.plist.PListXMLHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)}
117 | * .
118 | *
119 | * @throws SAXException
120 | */
121 | public void testStartElement_StringStringStringAttributes()
122 | throws SAXException {
123 | Attributes attrs = new AttributesImpl();
124 | assertNull(handler.getPlist());
125 |
126 | handler.setTempVal(new Stringer());
127 | handler.startElement(TAG_NAMESPACE, "plist", "plist", attrs);
128 | assertNotNull(handler.getPlist());
129 | }
130 |
131 | /**
132 | * Test method for
133 | * {@link com.longevitysoft.android.xml.plist.PListXMLHandler#endElement(java.lang.String, java.lang.String, java.lang.String)}
134 | * .
135 | *
136 | * @throws SAXException
137 | */
138 | public void testEndElementString_Unknown_String() throws SAXException {
139 | handler.startDocument();
140 | handler.endElement(TAG_NAMESPACE, "foo", "foo");
141 | assertNull(handler.getPlist());
142 | }
143 |
144 | /**
145 | * Test method for
146 | * {@link com.longevitysoft.android.xml.plist.PListXMLHandler#endElement(java.lang.String, java.lang.String, java.lang.String)}
147 | * .
148 | *
149 | * @throws SAXException
150 | */
151 | public void testEndElementString_PList_CallsListener() throws SAXException {
152 | mockListenerInvoked = false;
153 | PListParserListener mockListener = new PListXMLHandler.PListParserListener() {
154 |
155 | @Override
156 | public void onPListParseDone(PList list, ParseMode mode) {
157 | mockListenerInvoked = true;
158 | }
159 |
160 | };
161 | handler = new PListXMLHandler() {
162 |
163 | /*
164 | * (non-Javadoc)
165 | *
166 | * @see
167 | * com.longevitysoft.android.xml.plist.PListXMLHandler#startDocument
168 | * ()
169 | */
170 | @Override
171 | public void startDocument() throws SAXException {
172 | super.startDocument();
173 | super.setPlist(new PList());
174 | }
175 |
176 | };
177 | handler.setParseListener(mockListener);
178 | handler.startDocument();
179 | handler.endElement(TAG_NAMESPACE, "plist", "plist");
180 | assertNotNull(handler.getPlist());
181 | assertTrue(mockListenerInvoked);
182 | }
183 |
184 | protected static class MockPListXMLHandlerFullValid extends PListXMLHandler {
185 |
186 | /*
187 | * (non-Javadoc)
188 | *
189 | * @see
190 | * com.longevitysoft.android.xml.plist.PListXMLHandler#startDocument()
191 | */
192 | @Override
193 | public void startDocument() throws SAXException {
194 | super.startDocument();
195 | super.setPlist(new PList());
196 | Dict rootDict = new Dict();
197 | super.getPlist().setRootElement(rootDict);
198 | }
199 |
200 | public void setInArray(String key) throws SAXException {
201 | super.key = key;
202 | Attributes attrs = new AttributesImpl();
203 | super.startElement(TAG_NAMESPACE, "array", "array", attrs);
204 | }
205 |
206 | public void setInDict(String key) throws SAXException {
207 | super.key = key;
208 | Attributes attrs = new AttributesImpl();
209 | super.startElement(TAG_NAMESPACE, "dict", "dict", attrs);
210 | }
211 |
212 | };
213 |
214 | /**
215 | * Test method for
216 | * {@link com.longevitysoft.android.xml.plist.PListXMLHandler#endElement(java.lang.String, java.lang.String, java.lang.String)}
217 | * .
218 | *
219 | * @throws SAXException
220 | */
221 | // FIXME - this test no longer requires just setting tempval - but also the
222 | // state
223 | // of the handler's stack and its flags for parent element state
224 | public void testEndElementString_FullValidPList() throws SAXException {
225 | // setup test
226 | MockPListXMLHandlerFullValid mockHandler = new MockPListXMLHandlerFullValid();
227 | PList expected = new PList();
228 | Dict expectedDict = new Dict();
229 | Map dictMap = new HashMap();
230 | com.longevitysoft.android.xml.plist.domain.String v1 = new com.longevitysoft.android.xml.plist.domain.String();
231 | v1.setValue("string-val-1");
232 | Integer v2 = new Integer();
233 | v2.setValue(101);
234 | dictMap.put("key-1", v1);
235 | dictMap.put("key-2", v2);
236 | Array arr = new Array();
237 | com.longevitysoft.android.xml.plist.domain.String v3 = new com.longevitysoft.android.xml.plist.domain.String();
238 | v3.setValue("string-val-1");
239 | Integer v4 = new Integer();
240 | v4.setValue(102);
241 | arr.add(v2);
242 | arr.add(v4);
243 | dictMap.put("key-3", arr);
244 | arr = new Array();
245 | Map arrDictMap = new HashMap();
246 | com.longevitysoft.android.xml.plist.domain.String v5 = new com.longevitysoft.android.xml.plist.domain.String();
247 | v5.setValue("string-val-3");
248 | Integer v6 = new Integer();
249 | v6.setValue(103);
250 | arrDictMap.put("key-3", v5);
251 | arrDictMap.put("key-4", v6);
252 | Dict arrDict = new Dict();
253 | arrDict.setConfigMap(arrDictMap);
254 | arr.add(arrDict);
255 | dictMap.put("key-4", arr);
256 | expectedDict.setConfigMap(dictMap);
257 | expected.setRootElement(expectedDict);
258 | // exec test
259 | mockHandler.startDocument();
260 | mockHandler.setTempVal(new Stringer("key-1"));
261 | mockHandler.endElement(TAG_NAMESPACE, "key", "key");
262 | mockHandler.setTempVal(new Stringer("string-val-1"));
263 | mockHandler.endElement(TAG_NAMESPACE, "string", "string");
264 | mockHandler.setTempVal(new Stringer("key-2"));
265 | mockHandler.endElement(TAG_NAMESPACE, "key", "key");
266 | mockHandler.setTempVal(new Stringer("101"));
267 | mockHandler.endElement(TAG_NAMESPACE, "integer", "integer");
268 | // mock array mode
269 | mockHandler.setInArray("key-3");
270 | // populate array
271 | mockHandler.setTempVal(new Stringer("string-val-2"));
272 | mockHandler.endElement(TAG_NAMESPACE, "string", "string");
273 | mockHandler.setTempVal(new Stringer("102"));
274 | mockHandler.endElement(TAG_NAMESPACE, "integer", "integer");
275 | mockHandler.setTempVal(new Stringer(" \n "));
276 | mockHandler.endElement(TAG_NAMESPACE, "array", "array");
277 | // mock another array mode
278 | mockHandler.setInArray("key-4");
279 | // populate array with PList.DICT's
280 | mockHandler.setInDict("key-3");
281 | mockHandler.setTempVal(new Stringer("string-val-3"));
282 | mockHandler.endElement(TAG_NAMESPACE, "string", "string");
283 | mockHandler.endElement(TAG_NAMESPACE, "dict", "dict");
284 | mockHandler.setInDict("key-4");
285 | mockHandler.setTempVal(new Stringer("103"));
286 | mockHandler.endElement(TAG_NAMESPACE, "integer", "integer");
287 | mockHandler.endElement(TAG_NAMESPACE, "dict", "dict");
288 | mockHandler.setTempVal(new Stringer(" \n "));
289 | mockHandler.endElement(TAG_NAMESPACE, "array", "array");
290 | // wrap up
291 | mockHandler.endElement(TAG_NAMESPACE, "plist", "plist");
292 | assertNotNull(((Dict) mockHandler.getPlist().getRootElement())
293 | .getConfigMap());
294 | assertTrue(((Dict) expected.getRootElement()).getConfiguration("key-1")
295 | .equals(((Dict) mockHandler.getPlist().getRootElement())
296 | .getConfiguration("key-1")));
297 | assertNotNull(((Dict) mockHandler.getPlist().getRootElement())
298 | .getConfigurationArray("key-3"));
299 | assertEquals(2, ((Dict) mockHandler.getPlist().getRootElement())
300 | .getConfigurationArray("key-3").size());
301 | assertEquals(new java.lang.Integer(103), ((Dict) ((Dict) mockHandler
302 | .getPlist().getRootElement()).getConfigurationArray("key-4")
303 | .get(1)).getConfigurationInteger("key-4"));
304 | }
305 |
306 | }
307 |
--------------------------------------------------------------------------------
/android-plist-parser-test/src/com/longevitysoft/android/test/plist/xml/PListXMLParserTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Licensed under Creative Commons Attribution 3.0 Unported license.
3 | * http://creativecommons.org/licenses/by/3.0/
4 | * You are free to copy, distribute and transmit the work, and
5 | * to adapt the work. You must attribute android-plist-parser
6 | * to Free Beachler (http://www.freebeachler.com).
7 | *
8 | * The Android PList parser (android-plist-parser) is distributed in
9 | * the hope that it will be useful, but WITHOUT ANY WARRANTY; without
10 | * even the implied warranty of MERCHANTABILITY or FITNESS FOR A
11 | * PARTICULAR PURPOSE.
12 | */
13 | package com.longevitysoft.android.test.plist.xml;
14 |
15 | import java.io.ByteArrayInputStream;
16 | import java.io.IOException;
17 | import java.io.InputStream;
18 | import java.util.Date;
19 |
20 | import junit.framework.TestCase;
21 |
22 | import com.longevitysoft.android.xml.plist.PListXMLHandler;
23 | import com.longevitysoft.android.xml.plist.PListXMLParser;
24 | import com.longevitysoft.android.xml.plist.domain.Array;
25 | import com.longevitysoft.android.xml.plist.domain.Data;
26 | import com.longevitysoft.android.xml.plist.domain.Dict;
27 | import com.longevitysoft.android.xml.plist.domain.False;
28 | import com.longevitysoft.android.xml.plist.domain.PList;
29 | import com.longevitysoft.android.xml.plist.domain.Real;
30 | import com.longevitysoft.android.xml.plist.domain.True;
31 |
32 | /**
33 | * Tests {@link PListXMLParser} with various XML string fixtures.
34 | *
35 | * @author fbeachler
36 | *
37 | */
38 | public class PListXMLParserTest extends TestCase {
39 |
40 | public static final String INVALID_PLIST = ""
41 | + ""
42 | + ""
43 | + ""
44 | + "Galaxy Zoo Hubble primary classification workflow"
45 | + "1.0" + "" + "";
46 | public static final String VALID_WORKFLOW_VERSION_PLIST = ""
47 | + ""
48 | + ""
49 | + ""
50 | + "Galaxy Zoo Hubble primary classification workflow"
51 | + "1.0" + "" + "";
52 | public static final String VALID_WORKFLOW_PLIST = "\n"
53 | + "\n"
54 | + "\n"
55 | + "\n"
56 | + " workflow_answers\n"
57 | + " \n"
58 | + " \n"
59 | + " answer_id\n"
60 | + " 1\n"
61 | + " image_url\n"
62 | + " http://www.galaxyzoo.org/images/buttons/1_button.gif\n"
63 | + " next_workflow_task_id\n"
64 | + " 2\n"
65 | + " value\n"
66 | + " Smooth\n"
67 | + " workflow_answer_id\n"
68 | + " 1\n"
69 | + " workflow_task_id\n"
70 | + " 1\n"
71 | + " \n"
72 | + " \n"
73 | + " answer_id\n"
74 | + " 2\n"
75 | + " image_url\n"
76 | + " http://www.galaxyzoo.org/images/buttons/2_button.gif\n"
77 | + " next_workflow_task_id\n"
78 | + " 30\n"
79 | + " value\n"
80 | + " Features or disk\n"
81 | + " workflow_answer_id\n"
82 | + " 2\n"
83 | + " workflow_task_id\n"
84 | + " 1\n"
85 | + " \n"
86 | + " \n"
87 | + " answer_id\n"
88 | + " 3\n"
89 | + " image_url\n"
90 | + " http://www.galaxyzoo.org/images/buttons/3_button.gif\n"
91 | + " next_workflow_task_id\n"
92 | + " 0\n"
93 | + " value\n"
94 | + " Star or artifact\n"
95 | + " workflow_answer_id\n"
96 | + " 3\n"
97 | + " workflow_task_id\n"
98 | + " 1\n"
99 | + " \n"
100 | + " \n"
101 | + " answer_id\n"
102 | + " 16\n"
103 | + " image_url\n"
104 | + " http://www.galaxyzoo.org/images/buttons/16_button.gif\n"
105 | + " next_workflow_task_id\n"
106 | + " 3\n"
107 | + " value\n"
108 | + " Completely round\n"
109 | + " workflow_answer_id\n"
110 | + " 4\n"
111 | + " workflow_task_id\n"
112 | + " 2\n"
113 | + " \n"
114 | + " \n"
115 | + " answer_id\n"
116 | + " 20\n"
117 | + " image_url\n"
118 | + " http://www.galaxyzoo.org/images/buttons/20_button.gif\n"
119 | + " next_workflow_task_id\n"
120 | + " 0\n"
121 | + " value\n"
122 | + " Lens or arc\n"
123 | + " workflow_answer_id\n"
124 | + " 10\n"
125 | + " workflow_task_id\n"
126 | + " 4\n"
127 | + " \n"
128 | + " \n"
129 | + " answer_id\n"
130 | + " 21\n"
131 | + " image_url\n"
132 | + " http://www.galaxyzoo.org/images/buttons/21_button.gif\n"
133 | + " next_workflow_task_id\n"
134 | + " 0\n"
135 | + " value\n"
136 | + " Disturbed\n"
137 | + " workflow_answer_id\n"
138 | + " 11\n"
139 | + " workflow_task_id\n"
140 | + " 4\n"
141 | + " \n"
142 | + " \n"
143 | + " workflow_tasks\n"
144 | + " \n"
145 | + " \n"
146 | + " name\n"
147 | + " Is the galaxy simply smooth and rounded, with no sign of a disk?\n"
148 | + " parent_id\n"
149 | + " -1\n"
150 | + " task_id\n"
151 | + " 1\n"
152 | + " workflow_answers\n"
153 | + " \n"
154 | + " 1\n"
155 | + " 2\n"
156 | + " 3\n"
157 | + " \n"
158 | + " workflow_task_id\n"
159 | + " 1\n"
160 | + " \n"
161 | + " \n"
162 | + " name\n"
163 | + " How rounded is it?\n"
164 | + " parent_id\n"
165 | + " 1\n"
166 | + " task_id\n"
167 | + " 7\n"
168 | + " workflow_answers\n"
169 | + " \n"
170 | + " 4\n"
171 | + " 5\n"
172 | + " 6\n"
173 | + " \n"
174 | + " workflow_task_id\n"
175 | + " 2\n"
176 | + " \n"
177 | + " \n"
178 | + " name\n"
179 | + " Is there anything odd?\n"
180 | + " parent_id\n"
181 | + " 2\n"
182 | + " task_id\n"
183 | + " 6\n"
184 | + " workflow_answers\n"
185 | + " \n"
186 | + " 7\n"
187 | + " 8\n"
188 | + " \n"
189 | + " workflow_task_id\n"
190 | + " 3\n"
191 | + " \n"
192 | + " \n"
193 | + "\n" + "\n" + "";
194 | public static final String VALID_PLIST_ARRAY_ROOT = ""
195 | + ""
196 | + ""
197 | + ""
198 | + ""
199 | + "foo"
200 | + "1.0"
201 | + ""
202 | + ""
203 | + "bar"
204 | + "1.1"
205 | + ""
206 | + "" + "";
207 | public static final String VALID_PLIST_ARRAY_ROOT_NESTED_ARRAY = ""
208 | + ""
209 | + ""
210 | + ""
211 | + ""
212 | + "foo"
213 | + "bar"
214 | + ""
215 | + ""
216 | + "baz"
217 | + "quux"
218 | + "" + "" + "";
219 | public static final String VALID_PLIST_ARRAY_ROOT_NESTED_DICT = ""
220 | + ""
221 | + ""
222 | + ""
223 | + " \n"
224 | + " name\n"
225 | + " How rounded is it?\n"
226 | + " parent_id\n"
227 | + " 1\n"
228 | + " task_id\n"
229 | + " 7\n"
230 | + " workflow_answers\n"
231 | + " \n"
232 | + " 4\n"
233 | + " 5\n"
234 | + " 6\n"
235 | + " \n"
236 | + " workflow_task_id\n"
237 | + " 2\n"
238 | + " \n" + "" + "";
239 | public static final String VALID_PLIST_DICT_ROOT_NESTED_DICT = ""
240 | + ""
241 | + ""
242 | + "cat"
243 | + ""
244 | + "ID"
245 | + "901"
246 | + "title"
247 | + "Title"
248 | + "thumb"
249 | + ""
250 | + "ID"
251 | + "152"
252 | + "uri"
253 | + "http://www.google.com"
254 | + ""
255 | + "order"
256 | + "2"
257 | + "type"
258 | + "5" + "" + "";
259 | public static final String VALID_PLIST_STRING_ROOT = ""
260 | + ""
261 | + ""
262 | + ""
263 | + "foobar"
264 | + ""
265 | + "";
266 | public static final String VALID_PLIST_DATA_ROOT = ""
267 | + ""
268 | + ""
269 | + ""
270 | + "Zm9vYmFy"
271 | + ""
272 | + "";
273 | public static final String VALID_PLIST_DATE_ROOT = ""
274 | + ""
275 | + ""
276 | + ""
277 | + "Sun, 13 Feb 2011 12:01:00 GMT-0500" + "" + "";
278 | public static final String VALID_PLIST_ISO8601_DATE_ROOT = ""
279 | + ""
280 | + ""
281 | + ""
282 | + "2012-02-24T10:10:00Z"
283 | + "" + "";
284 | public static final String VALID_PLIST_REAL_ROOT = ""
285 | + ""
286 | + ""
287 | + ""
288 | + "3.1417"
289 | + ""
290 | + "";
291 | public static final String VALID_PLIST_INTEGER_ROOT = ""
292 | + ""
293 | + ""
294 | + ""
295 | + "1"
296 | + ""
297 | + "";
298 | public static final String VALID_PLIST_TRUE_ROOT = ""
299 | + ""
300 | + "" + "" + "";
301 | public static final String VALID_PLIST_FALSE_ROOT = ""
302 | + ""
303 | + "" + "" + "";
304 |
305 | /**
306 | * The class under test.
307 | */
308 | protected PListXMLParser parser;
309 |
310 | /*
311 | * (non-Javadoc)
312 | *
313 | * @see junit.framework.TestCase#setUp()
314 | */
315 | protected void setUp() throws Exception {
316 | super.setUp();
317 | parser = new PListXMLParser();
318 | }
319 |
320 | /*
321 | * (non-Javadoc)
322 | *
323 | * @see junit.framework.TestCase#tearDown()
324 | */
325 | protected void tearDown() throws Exception {
326 | parser = null;
327 | super.tearDown();
328 | }
329 |
330 | /**
331 | * Test method for
332 | * {@link com.longevitysoft.android.plist.xml.PListXMLParser#getHandler()}
333 | * and
334 | * {@link com.longevitysoft.android.plist.xml.PListXMLParser#setHandler(com.longevitysoft.android.test.plist.xml.PListXMLHandler)}
335 | * .
336 | */
337 | public void testHandlerGetterSetter() {
338 | assertNull(parser.getHandler());
339 | PListXMLHandler expected = new PListXMLHandler();
340 | parser.setHandler(expected);
341 | assertEquals(expected, parser.getHandler());
342 | }
343 |
344 | /**
345 | * Test method for
346 | * {@link com.longevitysoft.android.plist.xml.PListXMLParser#parse(java.lang.String)}
347 | * .
348 | */
349 | public void testParseWithNoHandler() {
350 | try {
351 | parser.parse("");
352 | } catch (Exception e) {
353 | assertEquals(IllegalStateException.class.getName(), e.getClass()
354 | .getName());
355 | return;
356 | }
357 | fail("expected exception not thrown");
358 | }
359 |
360 | /**
361 | * Test method for
362 | * {@link com.longevitysoft.android.plist.xml.PListXMLParser#parse(java.lang.String)}
363 | * .
364 | */
365 | public void testParseNull() {
366 | PListXMLHandler handler = new PListXMLHandler();
367 | parser.setHandler(handler);
368 | parser.parse("");
369 | assertNull(((PListXMLHandler) parser.getHandler()).getPlist());
370 | }
371 |
372 | /**
373 | * Test method for
374 | * {@link com.longevitysoft.android.plist.xml.PListXMLParser#parse(java.lang.String)}
375 | * .
376 | */
377 | public void testParseValidXMLInvalidPList() {
378 | PListXMLHandler handler = new PListXMLHandler();
379 | parser.setHandler(handler);
380 | parser.parse(INVALID_PLIST);
381 | assertNotNull(((PListXMLHandler) parser.getHandler()).getPlist());
382 | assertNull(((PListXMLHandler) parser.getHandler()).getPlist()
383 | .getRootElement());
384 | }
385 |
386 | /**
387 | * Test method for
388 | * {@link com.longevitysoft.android.plist.xml.PListXMLParser#parse(java.lang.String)}
389 | * .
390 | */
391 | public void testParseValidXMLWorkflowVersion() {
392 | PListXMLHandler handler = new PListXMLHandler();
393 | parser.setHandler(handler);
394 | parser.parse(VALID_WORKFLOW_VERSION_PLIST);
395 | assertNotNull(((PListXMLHandler) parser.getHandler()).getPlist());
396 | }
397 |
398 | /**
399 | * Test method for
400 | * {@link com.longevitysoft.android.plist.xml.PListXMLParser#parse(java.lang.String)}
401 | * .
402 | */
403 | public void testParseValidXMLWorkflow() {
404 | PListXMLHandler handler = new PListXMLHandler();
405 | parser.setHandler(handler);
406 | parser.parse(VALID_WORKFLOW_PLIST);
407 | PList actualPList = ((PListXMLHandler) parser.getHandler()).getPlist();
408 | assertNotNull(actualPList);
409 | assertEquals(6, ((Dict) actualPList.getRootElement())
410 | .getConfigurationArray("workflow_answers").size());
411 | assertEquals(3, ((Dict) actualPList.getRootElement())
412 | .getConfigurationArray("workflow_tasks").size());
413 | }
414 |
415 | /**
416 | * Test method for
417 | * {@link com.longevitysoft.android.plist.xml.PListXMLParser#parse(java.lang.String)}
418 | * .
419 | */
420 | public void testParseValidPListDictRootNestedDict() {
421 | PListXMLHandler handler = new PListXMLHandler();
422 | parser.setHandler(handler);
423 | parser.parse(VALID_PLIST_DICT_ROOT_NESTED_DICT);
424 | PList actualPList = ((PListXMLHandler) parser.getHandler()).getPlist();
425 | assertNotNull(actualPList);
426 | Dict cat = (Dict) actualPList.getRootElement();
427 | assertEquals("901", cat.getConfiguration("ID").getValue());
428 | assertEquals("Title", cat.getConfiguration("title").getValue());
429 | assertEquals(new Integer(152), cat.getConfigurationInteger("thumb.ID")
430 | .getValue());
431 | assertEquals("http://www.google.com", cat.getConfiguration("thumb.uri")
432 | .getValue());
433 | assertEquals(new Integer(2), cat.getConfigurationInteger("order")
434 | .getValue());
435 | assertEquals(new Integer(5), cat.getConfigurationInteger("type")
436 | .getValue());
437 | }
438 |
439 | /**
440 | * Test method for
441 | * {@link com.longevitysoft.android.plist.xml.PListXMLParser#parse(java.lang.String)}
442 | * .
443 | */
444 | public void testParseValidPListArrayRoot() {
445 | PListXMLHandler handler = new PListXMLHandler();
446 | parser.setHandler(handler);
447 | parser.parse(VALID_PLIST_ARRAY_ROOT);
448 | PList actualPList = ((PListXMLHandler) parser.getHandler()).getPlist();
449 | assertNotNull(actualPList);
450 | assertEquals(2, ((Array) actualPList.getRootElement()).size());
451 | }
452 |
453 | /**
454 | * Test method for
455 | * {@link com.longevitysoft.android.plist.xml.PListXMLParser#parse(java.lang.String)}
456 | * .
457 | */
458 | public void testParseValidPListArrayRootNestedArray() {
459 | PListXMLHandler handler = new PListXMLHandler();
460 | parser.setHandler(handler);
461 | parser.parse(VALID_PLIST_ARRAY_ROOT_NESTED_ARRAY);
462 | PList actualPList = ((PListXMLHandler) parser.getHandler()).getPlist();
463 | assertNotNull(actualPList);
464 | assertEquals(2, ((Array) actualPList.getRootElement()).size());
465 | assertEquals(2,
466 | ((Array) ((Array) actualPList.getRootElement()).get(0)).size());
467 | assertEquals(2,
468 | ((Array) ((Array) actualPList.getRootElement()).get(1)).size());
469 | }
470 |
471 | /**
472 | * Test method for
473 | * {@link com.longevitysoft.android.plist.xml.PListXMLParser#parse(java.lang.String)}
474 | * .
475 | */
476 | public void testParseValidPListStringRoot() {
477 | PListXMLHandler handler = new PListXMLHandler();
478 | parser.setHandler(handler);
479 | parser.parse(VALID_PLIST_STRING_ROOT);
480 | PList actualPList = ((PListXMLHandler) parser.getHandler()).getPlist();
481 | assertNotNull(actualPList);
482 | assertNotNull(actualPList.getRootElement());
483 | assertEquals(
484 | "foobar",
485 | ((com.longevitysoft.android.xml.plist.domain.String) actualPList
486 | .getRootElement()).getValue());
487 | }
488 |
489 | /**
490 | * Test method for
491 | * {@link com.longevitysoft.android.plist.xml.PListXMLParser#parse(java.lang.String)}
492 | * .
493 | */
494 | public void testParseValidPListDataRoot() {
495 | PListXMLHandler handler = new PListXMLHandler();
496 | parser.setHandler(handler);
497 | parser.parse(VALID_PLIST_DATA_ROOT);
498 | PList actualPList = ((PListXMLHandler) parser.getHandler()).getPlist();
499 | assertNotNull(actualPList);
500 | assertEquals("foobar", ((Data) actualPList.getRootElement()).getValue());
501 | }
502 |
503 | /**
504 | * Test method for
505 | * {@link com.longevitysoft.android.plist.xml.PListXMLParser#parse(java.lang.String)}
506 | * .
507 | */
508 | public void testParseValidPListISO8601DateRoot() {
509 | PListXMLHandler handler = new PListXMLHandler();
510 | parser.setHandler(handler);
511 | parser.parse(VALID_PLIST_ISO8601_DATE_ROOT);
512 | PList actualPList = ((PListXMLHandler) parser.getHandler()).getPlist();
513 | assertNotNull(actualPList);
514 | // 2012-02-24T10:10:00Z
515 | assertEquals(new Date("Fri, 24 Feb 2012 10:10:00 GMT-0700"),
516 | ((com.longevitysoft.android.xml.plist.domain.Date) actualPList
517 | .getRootElement()).getValue());
518 | }
519 |
520 | /**
521 | * Test method for
522 | * {@link com.longevitysoft.android.plist.xml.PListXMLParser#parse(java.lang.String)}
523 | * .
524 | */
525 | public void testParseValidPListDateRoot() {
526 | PListXMLHandler handler = new PListXMLHandler();
527 | parser.setHandler(handler);
528 | parser.parse(VALID_PLIST_DATE_ROOT);
529 | PList actualPList = ((PListXMLHandler) parser.getHandler()).getPlist();
530 | assertNotNull(actualPList);
531 | assertEquals(new Date("Sun, 13 Feb 2011 12:01:00 GMT-0500"),
532 | ((com.longevitysoft.android.xml.plist.domain.Date) actualPList
533 | .getRootElement()).getValue());
534 | }
535 |
536 | /**
537 | * Test method for
538 | * {@link com.longevitysoft.android.plist.xml.PListXMLParser#parse(java.lang.String)}
539 | * .
540 | */
541 | public void testParseValidPListRealRoot() {
542 | PListXMLHandler handler = new PListXMLHandler();
543 | parser.setHandler(handler);
544 | parser.parse(VALID_PLIST_REAL_ROOT);
545 | PList actualPList = ((PListXMLHandler) parser.getHandler()).getPlist();
546 | assertNotNull(actualPList);
547 | assertEquals(new Float(3.1417),
548 | ((Real) actualPList.getRootElement()).getValue());
549 | }
550 |
551 | /**
552 | * Test method for
553 | * {@link com.longevitysoft.android.plist.xml.PListXMLParser#parse(java.lang.String)}
554 | * .
555 | */
556 | public void testParseValidPListIntegerRoot() {
557 | PListXMLHandler handler = new PListXMLHandler();
558 | parser.setHandler(handler);
559 | parser.parse(VALID_PLIST_INTEGER_ROOT);
560 | PList actualPList = ((PListXMLHandler) parser.getHandler()).getPlist();
561 | assertNotNull(actualPList);
562 | assertEquals(
563 | new Integer(1).intValue(),
564 | ((com.longevitysoft.android.xml.plist.domain.Integer) actualPList
565 | .getRootElement()).getValue().intValue());
566 | }
567 |
568 | /**
569 | * Test method for
570 | * {@link com.longevitysoft.android.plist.xml.PListXMLParser#parse(java.lang.String)}
571 | * .
572 | */
573 | public void testParseValidPListTrueRoot() {
574 | PListXMLHandler handler = new PListXMLHandler();
575 | parser.setHandler(handler);
576 | parser.parse(VALID_PLIST_TRUE_ROOT);
577 | PList actualPList = ((PListXMLHandler) parser.getHandler()).getPlist();
578 | assertNotNull(actualPList);
579 | assertTrue(((True) actualPList.getRootElement()).getValue());
580 | }
581 |
582 | /**
583 | * Test method for
584 | * {@link com.longevitysoft.android.plist.xml.PListXMLParser#parse(java.lang.String)}
585 | * .
586 | */
587 | public void testParseValidPListFalseRoot() {
588 | PListXMLHandler handler = new PListXMLHandler();
589 | parser.setHandler(handler);
590 | parser.parse(VALID_PLIST_FALSE_ROOT);
591 | PList actualPList = ((PListXMLHandler) parser.getHandler()).getPlist();
592 | assertNotNull(actualPList);
593 | assertFalse(((False) actualPList.getRootElement()).getValue());
594 | }
595 |
596 | /**
597 | * Test method for
598 | * {@link com.longevitysoft.android.plist.xml.PListXMLParser#parse(java.io.InputStream)}
599 | * .
600 | *
601 | * @throws IOException
602 | * @throws IllegalStateException
603 | */
604 | public void testParseValidPListArrayRootAsInputStream()
605 | throws IllegalStateException, IOException {
606 | PListXMLHandler handler = new PListXMLHandler();
607 | parser.setHandler(handler);
608 | InputStream bas = new ByteArrayInputStream(
609 | VALID_PLIST_ARRAY_ROOT.getBytes());
610 | parser.parse(bas);
611 | PList actualPList = ((PListXMLHandler) parser.getHandler()).getPlist();
612 | assertNotNull(actualPList);
613 | assertEquals(2, ((Array) actualPList.getRootElement()).size());
614 | }
615 |
616 | }
617 |
--------------------------------------------------------------------------------
/android-plist-parser-app/src/net/sf/migbase64/Base64.java:
--------------------------------------------------------------------------------
1 | package net.sf.migbase64;
2 |
3 | import java.util.Arrays;
4 |
5 | /**
6 | * A very fast and memory efficient class to encode and decode to and from
7 | * BASE64 in full accordance with RFC 2045.
8 | *
9 | * On Windows XP sp1 with 1.4.2_04 and later ;), this encoder and decoder is
10 | * about 10 times faster on small arrays (10 - 1000 bytes) and 2-3 times as fast
11 | * on larger arrays (10000 - 1000000 bytes) compared to
12 | * sun.misc.Encoder()/Decoder().
13 | *
14 | *
15 | * On byte arrays the encoder is about 20% faster than Jakarta Commons Base64
16 | * Codec for encode and about 50% faster for decoding large arrays. This
17 | * implementation is about twice as fast on very small arrays (< 30 bytes). If
18 | * source/destination is a String this version is about three times
19 | * as fast due to the fact that the Commons Codec result has to be recoded to a
20 | * String from byte[], which is very expensive.
21 | *
22 | *
23 | * This encode/decode algorithm doesn't create any temporary arrays as many
24 | * other codecs do, it only allocates the resulting array. This produces less
25 | * garbage and it is possible to handle arrays twice as large as algorithms that
26 | * create a temporary array. (E.g. Jakarta Commons Codec). It is unknown whether
27 | * Sun's sun.misc.Encoder()/Decoder() produce temporary arrays but
28 | * since performance is quite low it probably does.
29 | *
30 | *
31 | * The encoder produces the same output as the Sun one except that the Sun's
32 | * encoder appends a trailing line separator if the last character isn't a pad.
33 | * Unclear why but it only adds to the length and is probably a side effect.
34 | * Both are in conformance with RFC 2045 though.
35 | * Commons codec seem to always att a trailing line separator.
36 | *
37 | *
38 | * Note! The encode/decode method pairs (types) come in three versions
39 | * with the exact same algorithm and thus a lot of code redundancy. This
40 | * is to not create any temporary arrays for transcoding to/from different
41 | * format types. The methods not used can simply be commented out.
42 | *
43 | *
44 | * There is also a "fast" version of all decode methods that works the same way
45 | * as the normal ones, but har a few demands on the decoded input. Normally
46 | * though, these fast verions should be used if the source if the input is known
47 | * and it hasn't bee tampered with.
48 | *
49 | *
50 | * If you find the code useful or you find a bug, please send me a note at
51 | * base64 @ miginfocom . com.
52 | *
53 | * Licence (BSD): ==============
54 | *
55 | * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (base64 @ miginfocom . com)
56 | * All rights reserved.
57 | *
58 | * Redistribution and use in source and binary forms, with or without
59 | * modification, are permitted provided that the following conditions are met:
60 | * Redistributions of source code must retain the above copyright notice, this
61 | * list of conditions and the following disclaimer. Redistributions in binary
62 | * form must reproduce the above copyright notice, this list of conditions and
63 | * the following disclaimer in the documentation and/or other materials provided
64 | * with the distribution. Neither the name of the MiG InfoCom AB nor the names
65 | * of its contributors may be used to endorse or promote products derived from
66 | * this software without specific prior written permission.
67 | *
68 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
69 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
70 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
71 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
72 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
73 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
74 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
75 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
76 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
77 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
78 | * POSSIBILITY OF SUCH DAMAGE.
79 | *
80 | * @version 2.2
81 | * @author Mikael Grev Date: 2004-aug-02 Time: 11:31:11
82 | */
83 |
84 | public class Base64 {
85 | private static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
86 | .toCharArray();
87 | private static final int[] IA = new int[256];
88 | static {
89 | Arrays.fill(IA, -1);
90 | for (int i = 0, iS = CA.length; i < iS; i++)
91 | IA[CA[i]] = i;
92 | IA['='] = 0;
93 | }
94 |
95 | // ****************************************************************************************
96 | // * char[] version
97 | // ****************************************************************************************
98 |
99 | /**
100 | * Encodes a raw byte array into a BASE64 char[] representation
101 | * i accordance with RFC 2045.
102 | *
103 | * @param sArr
104 | * The bytes to convert. If null or length 0 an
105 | * empty array will be returned.
106 | * @param lineSep
107 | * Optional "\r\n" after 76 characters, unless end of file.
108 | * No line separator will be in breach of RFC 2045 which
109 | * specifies max 76 per line but will be a little faster.
110 | * @return A BASE64 encoded array. Never null.
111 | */
112 | public final static char[] encodeToChar(byte[] sArr, boolean lineSep) {
113 | // Check special case
114 | int sLen = sArr != null ? sArr.length : 0;
115 | if (sLen == 0)
116 | return new char[0];
117 |
118 | int eLen = (sLen / 3) * 3; // Length of even 24-bits.
119 | int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count
120 | int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of
121 | // returned
122 | // array
123 | char[] dArr = new char[dLen];
124 |
125 | // Encode even 24-bits
126 | for (int s = 0, d = 0, cc = 0; s < eLen;) {
127 | // Copy next three bytes into lower 24 bits of int, paying attension
128 | // to sign.
129 | int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8
130 | | (sArr[s++] & 0xff);
131 |
132 | // Encode the int into four chars
133 | dArr[d++] = CA[(i >>> 18) & 0x3f];
134 | dArr[d++] = CA[(i >>> 12) & 0x3f];
135 | dArr[d++] = CA[(i >>> 6) & 0x3f];
136 | dArr[d++] = CA[i & 0x3f];
137 |
138 | // Add optional line separator
139 | if (lineSep && ++cc == 19 && d < dLen - 2) {
140 | dArr[d++] = '\r';
141 | dArr[d++] = '\n';
142 | cc = 0;
143 | }
144 | }
145 |
146 | // Pad and encode last bits if source isn't even 24 bits.
147 | int left = sLen - eLen; // 0 - 2.
148 | if (left > 0) {
149 | // Prepare the int
150 | int i = ((sArr[eLen] & 0xff) << 10)
151 | | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0);
152 |
153 | // Set last four chars
154 | dArr[dLen - 4] = CA[i >> 12];
155 | dArr[dLen - 3] = CA[(i >>> 6) & 0x3f];
156 | dArr[dLen - 2] = left == 2 ? CA[i & 0x3f] : '=';
157 | dArr[dLen - 1] = '=';
158 | }
159 | return dArr;
160 | }
161 |
162 | /**
163 | * Decodes a BASE64 encoded char array. All illegal characters will be
164 | * ignored and can handle both arrays with and without line separators.
165 | *
166 | * @param sArr
167 | * The source array. null or length 0 will return an
168 | * empty array.
169 | * @return The decoded array of bytes. May be of length 0. Will be
170 | * null if the legal characters (including '=') isn't
171 | * divideable by 4. (I.e. definitely corrupted).
172 | */
173 | public final static byte[] decode(char[] sArr) {
174 | // Check special case
175 | int sLen = sArr != null ? sArr.length : 0;
176 | if (sLen == 0)
177 | return new byte[0];
178 |
179 | // Count illegal characters (including '\r', '\n') to know what size the
180 | // returned array will be,
181 | // so we don't have to reallocate & copy it later.
182 | int sepCnt = 0; // Number of separator characters. (Actually illegal
183 | // characters, but that's a bonus...)
184 | for (int i = 0; i < sLen; i++)
185 | // If input is "pure" (I.e. no line separators or illegal chars)
186 | // base64 this loop can be commented out.
187 | if (IA[sArr[i]] < 0)
188 | sepCnt++;
189 |
190 | // Check so that legal chars (including '=') are evenly divideable by 4
191 | // as specified in RFC 2045.
192 | if ((sLen - sepCnt) % 4 != 0)
193 | return null;
194 |
195 | int pad = 0;
196 | for (int i = sLen; i > 1 && IA[sArr[--i]] <= 0;)
197 | if (sArr[i] == '=')
198 | pad++;
199 |
200 | int len = ((sLen - sepCnt) * 6 >> 3) - pad;
201 |
202 | byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
203 |
204 | for (int s = 0, d = 0; d < len;) {
205 | // Assemble three bytes into an int from four "valid" characters.
206 | int i = 0;
207 | for (int j = 0; j < 4; j++) { // j only increased if a valid char
208 | // was found.
209 | int c = IA[sArr[s++]];
210 | if (c >= 0)
211 | i |= c << (18 - j * 6);
212 | else
213 | j--;
214 | }
215 | // Add the bytes
216 | dArr[d++] = (byte) (i >> 16);
217 | if (d < len) {
218 | dArr[d++] = (byte) (i >> 8);
219 | if (d < len)
220 | dArr[d++] = (byte) i;
221 | }
222 | }
223 | return dArr;
224 | }
225 |
226 | /**
227 | * Decodes a BASE64 encoded char array that is known to be resonably well
228 | * formatted. The method is about twice as fast as {@link #decode(char[])}.
229 | * The preconditions are:
230 | * + The array must have a line length of 76 chars OR no line separators at
231 | * all (one line).
232 | * + Line separator must be "\r\n", as specified in RFC 2045 + The array
233 | * must not contain illegal characters within the encoded string
234 | * + The array CAN have illegal characters at the beginning and end, those
235 | * will be dealt with appropriately.
236 | *
237 | * @param sArr
238 | * The source array. Length 0 will return an empty array.
239 | * null will throw an exception.
240 | * @return The decoded array of bytes. May be of length 0.
241 | */
242 | public final static byte[] decodeFast(char[] sArr) {
243 | // Check special case
244 | int sLen = sArr.length;
245 | if (sLen == 0)
246 | return new byte[0];
247 |
248 | int sIx = 0, eIx = sLen - 1; // Start and end index after trimming.
249 |
250 | // Trim illegal chars from start
251 | while (sIx < eIx && IA[sArr[sIx]] < 0)
252 | sIx++;
253 |
254 | // Trim illegal chars from end
255 | while (eIx > 0 && IA[sArr[eIx]] < 0)
256 | eIx--;
257 |
258 | // get the padding count (=) (0, 1 or 2)
259 | int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count
260 | // '='
261 | // at
262 | // end.
263 | int cCnt = eIx - sIx + 1; // Content count including possible separators
264 | int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0;
265 |
266 | int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded
267 | // bytes
268 | byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
269 |
270 | // Decode all but the last 0 - 2 bytes.
271 | int d = 0;
272 | for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
273 | // Assemble three bytes into an int from four "valid" characters.
274 | int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12
275 | | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]];
276 |
277 | // Add the bytes
278 | dArr[d++] = (byte) (i >> 16);
279 | dArr[d++] = (byte) (i >> 8);
280 | dArr[d++] = (byte) i;
281 |
282 | // If line separator, jump over it.
283 | if (sepCnt > 0 && ++cc == 19) {
284 | sIx += 2;
285 | cc = 0;
286 | }
287 | }
288 |
289 | if (d < len) {
290 | // Decode last 1-3 bytes (incl '=') into 1-3 bytes
291 | int i = 0;
292 | for (int j = 0; sIx <= eIx - pad; j++)
293 | i |= IA[sArr[sIx++]] << (18 - j * 6);
294 |
295 | for (int r = 16; d < len; r -= 8)
296 | dArr[d++] = (byte) (i >> r);
297 | }
298 |
299 | return dArr;
300 | }
301 |
302 | // ****************************************************************************************
303 | // * byte[] version
304 | // ****************************************************************************************
305 |
306 | /**
307 | * Encodes a raw byte array into a BASE64 byte[] representation
308 | * i accordance with RFC 2045.
309 | *
310 | * @param sArr
311 | * The bytes to convert. If null or length 0 an
312 | * empty array will be returned.
313 | * @param lineSep
314 | * Optional "\r\n" after 76 characters, unless end of file.
315 | * No line separator will be in breach of RFC 2045 which
316 | * specifies max 76 per line but will be a little faster.
317 | * @return A BASE64 encoded array. Never null.
318 | */
319 | public final static byte[] encodeToByte(byte[] sArr, boolean lineSep) {
320 | // Check special case
321 | int sLen = sArr != null ? sArr.length : 0;
322 | if (sLen == 0)
323 | return new byte[0];
324 |
325 | int eLen = (sLen / 3) * 3; // Length of even 24-bits.
326 | int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count
327 | int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of
328 | // returned
329 | // array
330 | byte[] dArr = new byte[dLen];
331 |
332 | // Encode even 24-bits
333 | for (int s = 0, d = 0, cc = 0; s < eLen;) {
334 | // Copy next three bytes into lower 24 bits of int, paying attension
335 | // to sign.
336 | int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8
337 | | (sArr[s++] & 0xff);
338 |
339 | // Encode the int into four chars
340 | dArr[d++] = (byte) CA[(i >>> 18) & 0x3f];
341 | dArr[d++] = (byte) CA[(i >>> 12) & 0x3f];
342 | dArr[d++] = (byte) CA[(i >>> 6) & 0x3f];
343 | dArr[d++] = (byte) CA[i & 0x3f];
344 |
345 | // Add optional line separator
346 | if (lineSep && ++cc == 19 && d < dLen - 2) {
347 | dArr[d++] = '\r';
348 | dArr[d++] = '\n';
349 | cc = 0;
350 | }
351 | }
352 |
353 | // Pad and encode last bits if source isn't an even 24 bits.
354 | int left = sLen - eLen; // 0 - 2.
355 | if (left > 0) {
356 | // Prepare the int
357 | int i = ((sArr[eLen] & 0xff) << 10)
358 | | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0);
359 |
360 | // Set last four chars
361 | dArr[dLen - 4] = (byte) CA[i >> 12];
362 | dArr[dLen - 3] = (byte) CA[(i >>> 6) & 0x3f];
363 | dArr[dLen - 2] = left == 2 ? (byte) CA[i & 0x3f] : (byte) '=';
364 | dArr[dLen - 1] = '=';
365 | }
366 | return dArr;
367 | }
368 |
369 | /**
370 | * Decodes a BASE64 encoded byte array. All illegal characters will be
371 | * ignored and can handle both arrays with and without line separators.
372 | *
373 | * @param sArr
374 | * The source array. Length 0 will return an empty array.
375 | * null will throw an exception.
376 | * @return The decoded array of bytes. May be of length 0. Will be
377 | * null if the legal characters (including '=') isn't
378 | * divideable by 4. (I.e. definitely corrupted).
379 | */
380 | public final static byte[] decode(byte[] sArr) {
381 | // Check special case
382 | int sLen = sArr.length;
383 |
384 | // Count illegal characters (including '\r', '\n') to know what size the
385 | // returned array will be,
386 | // so we don't have to reallocate & copy it later.
387 | int sepCnt = 0; // Number of separator characters. (Actually illegal
388 | // characters, but that's a bonus...)
389 | for (int i = 0; i < sLen; i++)
390 | // If input is "pure" (I.e. no line separators or illegal chars)
391 | // base64 this loop can be commented out.
392 | if (IA[sArr[i] & 0xff] < 0)
393 | sepCnt++;
394 |
395 | // Check so that legal chars (including '=') are evenly divideable by 4
396 | // as specified in RFC 2045.
397 | if ((sLen - sepCnt) % 4 != 0)
398 | return null;
399 |
400 | int pad = 0;
401 | for (int i = sLen; i > 1 && IA[sArr[--i] & 0xff] <= 0;)
402 | if (sArr[i] == '=')
403 | pad++;
404 |
405 | int len = ((sLen - sepCnt) * 6 >> 3) - pad;
406 |
407 | byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
408 |
409 | for (int s = 0, d = 0; d < len;) {
410 | // Assemble three bytes into an int from four "valid" characters.
411 | int i = 0;
412 | for (int j = 0; j < 4; j++) { // j only increased if a valid char
413 | // was found.
414 | int c = IA[sArr[s++] & 0xff];
415 | if (c >= 0)
416 | i |= c << (18 - j * 6);
417 | else
418 | j--;
419 | }
420 |
421 | // Add the bytes
422 | dArr[d++] = (byte) (i >> 16);
423 | if (d < len) {
424 | dArr[d++] = (byte) (i >> 8);
425 | if (d < len)
426 | dArr[d++] = (byte) i;
427 | }
428 | }
429 |
430 | return dArr;
431 | }
432 |
433 | /**
434 | * Decodes a BASE64 encoded byte array that is known to be resonably well
435 | * formatted. The method is about twice as fast as {@link #decode(byte[])}.
436 | * The preconditions are:
437 | * + The array must have a line length of 76 chars OR no line separators at
438 | * all (one line).
439 | * + Line separator must be "\r\n", as specified in RFC 2045 + The array
440 | * must not contain illegal characters within the encoded string
441 | * + The array CAN have illegal characters at the beginning and end, those
442 | * will be dealt with appropriately.
443 | *
444 | * @param sArr
445 | * The source array. Length 0 will return an empty array.
446 | * null will throw an exception.
447 | * @return The decoded array of bytes. May be of length 0.
448 | */
449 | public final static byte[] decodeFast(byte[] sArr) {
450 | // Check special case
451 | int sLen = sArr.length;
452 | if (sLen == 0)
453 | return new byte[0];
454 |
455 | int sIx = 0, eIx = sLen - 1; // Start and end index after trimming.
456 |
457 | // Trim illegal chars from start
458 | while (sIx < eIx && IA[sArr[sIx] & 0xff] < 0)
459 | sIx++;
460 |
461 | // Trim illegal chars from end
462 | while (eIx > 0 && IA[sArr[eIx] & 0xff] < 0)
463 | eIx--;
464 |
465 | // get the padding count (=) (0, 1 or 2)
466 | int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count
467 | // '='
468 | // at
469 | // end.
470 | int cCnt = eIx - sIx + 1; // Content count including possible separators
471 | int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0;
472 |
473 | int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded
474 | // bytes
475 | byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
476 |
477 | // Decode all but the last 0 - 2 bytes.
478 | int d = 0;
479 | for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
480 | // Assemble three bytes into an int from four "valid" characters.
481 | int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12
482 | | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]];
483 |
484 | // Add the bytes
485 | dArr[d++] = (byte) (i >> 16);
486 | dArr[d++] = (byte) (i >> 8);
487 | dArr[d++] = (byte) i;
488 |
489 | // If line separator, jump over it.
490 | if (sepCnt > 0 && ++cc == 19) {
491 | sIx += 2;
492 | cc = 0;
493 | }
494 | }
495 |
496 | if (d < len) {
497 | // Decode last 1-3 bytes (incl '=') into 1-3 bytes
498 | int i = 0;
499 | for (int j = 0; sIx <= eIx - pad; j++)
500 | i |= IA[sArr[sIx++]] << (18 - j * 6);
501 |
502 | for (int r = 16; d < len; r -= 8)
503 | dArr[d++] = (byte) (i >> r);
504 | }
505 |
506 | return dArr;
507 | }
508 |
509 | // ****************************************************************************************
510 | // * String version
511 | // ****************************************************************************************
512 |
513 | /**
514 | * Encodes a raw byte array into a BASE64 String representation
515 | * i accordance with RFC 2045.
516 | *
517 | * @param sArr
518 | * The bytes to convert. If null or length 0 an
519 | * empty array will be returned.
520 | * @param lineSep
521 | * Optional "\r\n" after 76 characters, unless end of file.
522 | * No line separator will be in breach of RFC 2045 which
523 | * specifies max 76 per line but will be a little faster.
524 | * @return A BASE64 encoded array. Never null.
525 | */
526 | public final static String encodeToString(byte[] sArr, boolean lineSep) {
527 | // Reuse char[] since we can't create a String incrementally anyway and
528 | // StringBuffer/Builder would be slower.
529 | return new String(encodeToChar(sArr, lineSep));
530 | }
531 |
532 | /**
533 | * Decodes a BASE64 encoded String. All illegal characters will
534 | * be ignored and can handle both strings with and without line separators.
535 | * Note! It can be up to about 2x the speed to call
536 | * decode(str.toCharArray()) instead. That will create a
537 | * temporary array though. This version will use str.charAt(i)
538 | * to iterate the string.
539 | *
540 | * @param str
541 | * The source string. null or length 0 will return
542 | * an empty array.
543 | * @return The decoded array of bytes. May be of length 0. Will be
544 | * null if the legal characters (including '=') isn't
545 | * divideable by 4. (I.e. definitely corrupted).
546 | */
547 | public final static byte[] decode(String str) {
548 | // Check special case
549 | int sLen = str != null ? str.length() : 0;
550 | if (sLen == 0)
551 | return new byte[0];
552 |
553 | // Count illegal characters (including '\r', '\n') to know what size the
554 | // returned array will be,
555 | // so we don't have to reallocate & copy it later.
556 | int sepCnt = 0; // Number of separator characters. (Actually illegal
557 | // characters, but that's a bonus...)
558 | for (int i = 0; i < sLen; i++)
559 | // If input is "pure" (I.e. no line separators or illegal chars)
560 | // base64 this loop can be commented out.
561 | if (IA[str.charAt(i)] < 0)
562 | sepCnt++;
563 |
564 | // Check so that legal chars (including '=') are evenly divideable by 4
565 | // as specified in RFC 2045.
566 | if ((sLen - sepCnt) % 4 != 0)
567 | return null;
568 |
569 | // Count '=' at end
570 | int pad = 0;
571 | for (int i = sLen; i > 1 && IA[str.charAt(--i)] <= 0;)
572 | if (str.charAt(i) == '=')
573 | pad++;
574 |
575 | int len = ((sLen - sepCnt) * 6 >> 3) - pad;
576 |
577 | byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
578 |
579 | for (int s = 0, d = 0; d < len;) {
580 | // Assemble three bytes into an int from four "valid" characters.
581 | int i = 0;
582 | for (int j = 0; j < 4; j++) { // j only increased if a valid char
583 | // was found.
584 | int c = IA[str.charAt(s++)];
585 | if (c >= 0)
586 | i |= c << (18 - j * 6);
587 | else
588 | j--;
589 | }
590 | // Add the bytes
591 | dArr[d++] = (byte) (i >> 16);
592 | if (d < len) {
593 | dArr[d++] = (byte) (i >> 8);
594 | if (d < len)
595 | dArr[d++] = (byte) i;
596 | }
597 | }
598 | return dArr;
599 | }
600 |
601 | /**
602 | * Decodes a BASE64 encoded string that is known to be resonably well
603 | * formatted. The method is about twice as fast as {@link #decode(String)}.
604 | * The preconditions are:
605 | * + The array must have a line length of 76 chars OR no line separators at
606 | * all (one line).
607 | * + Line separator must be "\r\n", as specified in RFC 2045 + The array
608 | * must not contain illegal characters within the encoded string
609 | * + The array CAN have illegal characters at the beginning and end, those
610 | * will be dealt with appropriately.
611 | *
612 | * @param s
613 | * The source string. Length 0 will return an empty array.
614 | * null will throw an exception.
615 | * @return The decoded array of bytes. May be of length 0.
616 | */
617 | public final static byte[] decodeFast(String s) {
618 | // Check special case
619 | int sLen = s.length();
620 | if (sLen == 0)
621 | return new byte[0];
622 |
623 | int sIx = 0, eIx = sLen - 1; // Start and end index after trimming.
624 |
625 | // Trim illegal chars from start
626 | while (sIx < eIx && IA[s.charAt(sIx) & 0xff] < 0)
627 | sIx++;
628 |
629 | // Trim illegal chars from end
630 | while (eIx > 0 && IA[s.charAt(eIx) & 0xff] < 0)
631 | eIx--;
632 |
633 | // get the padding count (=) (0, 1 or 2)
634 | int pad = s.charAt(eIx) == '=' ? (s.charAt(eIx - 1) == '=' ? 2 : 1) : 0; // Count
635 | // '='
636 | // at
637 | // end.
638 | int cCnt = eIx - sIx + 1; // Content count including possible separators
639 | int sepCnt = sLen > 76 ? (s.charAt(76) == '\r' ? cCnt / 78 : 0) << 1
640 | : 0;
641 |
642 | int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded
643 | // bytes
644 | byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
645 |
646 | // Decode all but the last 0 - 2 bytes.
647 | int d = 0;
648 | for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
649 | // Assemble three bytes into an int from four "valid" characters.
650 | int i = IA[s.charAt(sIx++)] << 18 | IA[s.charAt(sIx++)] << 12
651 | | IA[s.charAt(sIx++)] << 6 | IA[s.charAt(sIx++)];
652 |
653 | // Add the bytes
654 | dArr[d++] = (byte) (i >> 16);
655 | dArr[d++] = (byte) (i >> 8);
656 | dArr[d++] = (byte) i;
657 |
658 | // If line separator, jump over it.
659 | if (sepCnt > 0 && ++cc == 19) {
660 | sIx += 2;
661 | cc = 0;
662 | }
663 | }
664 |
665 | if (d < len) {
666 | // Decode last 1-3 bytes (incl '=') into 1-3 bytes
667 | int i = 0;
668 | for (int j = 0; sIx <= eIx - pad; j++)
669 | i |= IA[s.charAt(sIx++)] << (18 - j * 6);
670 |
671 | for (int r = 16; d < len; r -= 8)
672 | dArr[d++] = (byte) (i >> r);
673 | }
674 |
675 | return dArr;
676 | }
677 | }
--------------------------------------------------------------------------------