├── .gitignore ├── android-plist-parser-app ├── .gitignore ├── res │ ├── drawable-hdpi │ │ └── icon.png │ ├── drawable-ldpi │ │ └── icon.png │ ├── drawable-mdpi │ │ └── icon.png │ ├── values │ │ └── strings.xml │ └── layout │ │ └── main.xml ├── .classpath ├── AndroidManifest.xml ├── default.properties ├── project.properties ├── AndroidManifest_UnFiltered.xml ├── .project ├── src │ ├── com │ │ └── longevitysoft │ │ │ └── android │ │ │ ├── xml │ │ │ └── plist │ │ │ │ ├── domain │ │ │ │ ├── String.java │ │ │ │ ├── True.java │ │ │ │ ├── PListObjectType.java │ │ │ │ ├── IPListSimpleObject.java │ │ │ │ ├── PListObject.java │ │ │ │ ├── False.java │ │ │ │ ├── Real.java │ │ │ │ ├── Integer.java │ │ │ │ ├── Date.java │ │ │ │ ├── Data.java │ │ │ │ ├── Dict.java │ │ │ │ ├── Array.java │ │ │ │ └── PList.java │ │ │ │ ├── Constants.java │ │ │ │ ├── PListXMLParser.java │ │ │ │ ├── BaseXMLParser.java │ │ │ │ └── PListXMLHandler.java │ │ │ ├── DumActivity.java │ │ │ └── util │ │ │ └── Stringer.java │ └── net │ │ └── sf │ │ └── migbase64 │ │ └── Base64.java ├── pom.xml └── .settings │ └── org.eclipse.jdt.ui.prefs ├── android-plist-parser-test ├── .gitignore ├── res │ ├── drawable-hdpi │ │ └── icon.png │ ├── drawable-ldpi │ │ └── icon.png │ ├── drawable-mdpi │ │ └── icon.png │ ├── values │ │ └── strings.xml │ └── layout │ │ └── main.xml ├── .classpath ├── project.properties ├── default.properties ├── AndroidManifest.xml ├── .project ├── src │ └── com │ │ └── longevitysoft │ │ └── android │ │ └── test │ │ └── plist │ │ ├── AllTests.java │ │ ├── InstrumentationTestRunner.java │ │ └── xml │ │ ├── PListXMLHandlerTest.java │ │ └── PListXMLParserTest.java ├── pom.xml └── .settings │ └── org.eclipse.jdt.ui.prefs ├── README.TXT ├── LICENSE.TXT └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | **/local.properties 2 | **/gen 3 | **/gen/** 4 | -------------------------------------------------------------------------------- /android-plist-parser-app/.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | gen 3 | target 4 | -------------------------------------------------------------------------------- /android-plist-parser-test/.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | gen 3 | target 4 | -------------------------------------------------------------------------------- /android-plist-parser-app/res/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tenaciousRas/android-plist-parser/HEAD/android-plist-parser-app/res/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /android-plist-parser-app/res/drawable-ldpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tenaciousRas/android-plist-parser/HEAD/android-plist-parser-app/res/drawable-ldpi/icon.png -------------------------------------------------------------------------------- /android-plist-parser-app/res/drawable-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tenaciousRas/android-plist-parser/HEAD/android-plist-parser-app/res/drawable-mdpi/icon.png -------------------------------------------------------------------------------- /android-plist-parser-test/res/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tenaciousRas/android-plist-parser/HEAD/android-plist-parser-test/res/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /android-plist-parser-test/res/drawable-ldpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tenaciousRas/android-plist-parser/HEAD/android-plist-parser-test/res/drawable-ldpi/icon.png -------------------------------------------------------------------------------- /android-plist-parser-test/res/drawable-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tenaciousRas/android-plist-parser/HEAD/android-plist-parser-test/res/drawable-mdpi/icon.png -------------------------------------------------------------------------------- /android-plist-parser-app/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello World! 4 | android plist app source 5 | 6 | -------------------------------------------------------------------------------- /android-plist-parser-test/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello World! 4 | android plist app sourceTest 5 | 6 | -------------------------------------------------------------------------------- /android-plist-parser-app/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android-plist-parser-test/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android-plist-parser-app/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android-plist-parser-app/default.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system use, 7 | # "build.properties", and override values to adapt the script to your 8 | # project structure. 9 | 10 | # Project target. 11 | target=android-4 12 | -------------------------------------------------------------------------------- /android-plist-parser-app/project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system use, 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | 10 | # Project target. 11 | target=android-4 12 | -------------------------------------------------------------------------------- /android-plist-parser-test/project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system use, 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | 10 | # Project target. 11 | target=android-4 12 | -------------------------------------------------------------------------------- /android-plist-parser-test/default.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system use, 7 | # "build.properties", and override values to adapt the script to your 8 | # project structure. 9 | 10 | # Project target. 11 | target=android-4 12 | -------------------------------------------------------------------------------- /android-plist-parser-app/res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android-plist-parser-test/res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android-plist-parser-app/AndroidManifest_UnFiltered.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android-plist-parser-test/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | -------------------------------------------------------------------------------- /android-plist-parser-app/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | android-plist-parser-app 4 | 5 | 6 | 7 | 8 | 9 | com.android.ide.eclipse.adt.ResourceManagerBuilder 10 | 11 | 12 | 13 | 14 | com.android.ide.eclipse.adt.PreCompilerBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.jdt.core.javabuilder 20 | 21 | 22 | 23 | 24 | com.android.ide.eclipse.adt.ApkBuilder 25 | 26 | 27 | 28 | 29 | 30 | com.android.ide.eclipse.adt.AndroidNature 31 | org.eclipse.jdt.core.javanature 32 | 33 | 34 | -------------------------------------------------------------------------------- /android-plist-parser-test/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | android-plist-parser-test 4 | 5 | 6 | android-plist-app 7 | 8 | 9 | 10 | com.android.ide.eclipse.adt.ResourceManagerBuilder 11 | 12 | 13 | 14 | 15 | com.android.ide.eclipse.adt.PreCompilerBuilder 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javabuilder 21 | 22 | 23 | 24 | 25 | com.android.ide.eclipse.adt.ApkBuilder 26 | 27 | 28 | 29 | 30 | 31 | com.android.ide.eclipse.adt.AndroidNature 32 | org.eclipse.jdt.core.javanature 33 | 34 | 35 | -------------------------------------------------------------------------------- /android-plist-parser-test/src/com/longevitysoft/android/test/plist/AllTests.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; 14 | 15 | import junit.framework.Test; 16 | import junit.framework.TestSuite; 17 | import android.test.suitebuilder.TestSuiteBuilder; 18 | 19 | /** 20 | * A test suite that contains all tests for the plist parser. 21 | */ 22 | public class AllTests extends TestSuite { 23 | 24 | public static Test suite() { 25 | return new TestSuiteBuilder(AllTests.class) 26 | .includeAllPackagesUnderHere().build(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /android-plist-parser-app/src/com/longevitysoft/android/xml/plist/domain/String.java: -------------------------------------------------------------------------------- 1 | package com.longevitysoft.android.xml.plist.domain; 2 | 3 | import com.longevitysoft.android.util.Stringer; 4 | 5 | /** 6 | * Represents a simple plist string element. Not to be confused with 7 | * {@link java.lang.String}. 8 | */ 9 | public class String extends PListObject implements 10 | IPListSimpleObject { 11 | 12 | protected Stringer str; 13 | 14 | /** 15 | * 16 | */ 17 | private static final long serialVersionUID = -8134261357175236382L; 18 | 19 | public String() { 20 | setType(PListObjectType.STRING); 21 | str = new Stringer(); 22 | } 23 | 24 | /* 25 | * (non-Javadoc) 26 | * 27 | * @see 28 | * com.longevitysoft.android.xml.plist.domain.IPListSimpleObject#getValue() 29 | */ 30 | @Override 31 | public java.lang.String getValue() { 32 | return this.str.getBuilder().toString(); 33 | } 34 | 35 | /* 36 | * (non-Javadoc) 37 | * 38 | * @see 39 | * com.longevitysoft.android.xml.plist.domain.IPListSimpleObject#setValue 40 | * (java.lang.Object) 41 | */ 42 | @Override 43 | public void setValue(java.lang.String val) { 44 | str.newBuilder().append(val); 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /android-plist-parser-app/src/com/longevitysoft/android/xml/plist/domain/True.java: -------------------------------------------------------------------------------- 1 | package com.longevitysoft.android.xml.plist.domain; 2 | 3 | /** 4 | * Represents a simple plist true element. 5 | */ 6 | public class True extends PListObject implements IPListSimpleObject { 7 | 8 | /** 9 | * 10 | */ 11 | private static final long serialVersionUID = -3560354198720649001L; 12 | 13 | public True() { 14 | setType(PListObjectType.TRUE); 15 | } 16 | 17 | /* 18 | * (non-Javadoc) 19 | * 20 | * @see 21 | * com.longevitysoft.android.xml.plist.domain.IPListSimpleObject#getValue() 22 | */ 23 | @Override 24 | public Boolean getValue() { 25 | return new Boolean(true); 26 | } 27 | 28 | /* 29 | * (non-Javadoc) 30 | * 31 | * @see 32 | * com.longevitysoft.android.xml.plist.domain.IPListSimpleObject#setValue 33 | * (java.lang.Object) 34 | */ 35 | @Override 36 | public void setValue(Boolean val) { 37 | // noop 38 | } 39 | 40 | /* 41 | * (non-Javadoc) 42 | * 43 | * @see 44 | * com.longevitysoft.android.xml.plist.domain.IPListSimpleObject#setValue 45 | * (java.lang.String) 46 | */ 47 | @Override 48 | public void setValue(java.lang.String val) { 49 | // noop 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /android-plist-parser-app/src/com/longevitysoft/android/DumActivity.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; 14 | import android.app.Activity; 15 | import android.os.Bundle; 16 | 17 | /** 18 | * 19 | */ 20 | 21 | /** 22 | * Here for the purpose of instrumentation. For example of how to use the PList 23 | * parser, refer to the android-plist-parser-test instrumentation project. 24 | */ 25 | public class DumActivity extends Activity { 26 | 27 | /* 28 | * (non-Javadoc) 29 | * 30 | * @see android.app.Activity#onCreate(android.os.Bundle) 31 | */ 32 | @Override 33 | protected void onCreate(Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /android-plist-parser-app/src/com/longevitysoft/android/xml/plist/domain/PListObjectType.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 | /** 16 | * Defines valid PList object types. These correspond to the 17 | * elements defined in the PList XML DTD at {@link http 18 | * ://www.apple.com/DTDs/PropertyList-1.0.dtd}. 19 | * 20 | * @author fbeachler 21 | * 22 | */ 23 | public enum PListObjectType { 24 | ARRAY(0), DATA(1), DATE(2), DICT(3), REAL(4), INTEGER(5), STRING( 25 | 6), TRUE(7), FALSE(8); 26 | 27 | private int type; 28 | 29 | private PListObjectType(int type) { 30 | this.type = type; 31 | } 32 | 33 | public int getType() { 34 | return this.type; 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /android-plist-parser-app/src/com/longevitysoft/android/xml/plist/domain/IPListSimpleObject.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 | /** 16 | * Interface that simple PList objects implement. This includes all objects 17 | * besides from {@link Array}s and {@link Dict}s. 18 | */ 19 | public interface IPListSimpleObject { 20 | 21 | /** 22 | * Get the value of the plist object. 23 | * 24 | * @return 25 | */ 26 | public E getValue(); 27 | 28 | /** 29 | * Set the value of the PList object. 30 | * 31 | * @param val 32 | */ 33 | public void setValue(E val); 34 | 35 | /** 36 | * Set the value of the PList object from a string. 37 | * 38 | * @param val 39 | */ 40 | public void setValue(java.lang.String val); 41 | } -------------------------------------------------------------------------------- /android-plist-parser-app/src/com/longevitysoft/android/xml/plist/domain/PListObject.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.io.Serializable; 16 | 17 | /** 18 | * A PListObject is an object which has a valid {@link PListObjectType}. 19 | */ 20 | public class PListObject extends Object implements Cloneable, 21 | Serializable { 22 | 23 | /** 24 | * 25 | */ 26 | private static final long serialVersionUID = -5258056855425643835L; 27 | 28 | private PListObjectType type; 29 | 30 | /** 31 | * @return the type 32 | */ 33 | public PListObjectType getType() { 34 | return type; 35 | } 36 | 37 | /** 38 | * @param type 39 | * the type to set 40 | */ 41 | public void setType(PListObjectType type) { 42 | this.type = type; 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /android-plist-parser-test/src/com/longevitysoft/android/test/plist/InstrumentationTestRunner.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; 14 | 15 | import junit.framework.TestSuite; 16 | import android.test.suitebuilder.TestSuiteBuilder; 17 | 18 | /** 19 | * A instrumentation runner that contains all tests for the plist parser. 20 | */ 21 | public class InstrumentationTestRunner extends 22 | android.test.InstrumentationTestRunner { 23 | 24 | @Override 25 | public TestSuite getAllTests() { 26 | return new TestSuiteBuilder(InstrumentationTestRunner.class) 27 | .includePackages( 28 | "com.longevitysoft.android.test.plist.xml") 29 | .build(); 30 | } 31 | 32 | @Override 33 | public ClassLoader getLoader() { 34 | return InstrumentationTestRunner.class.getClassLoader(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /android-plist-parser-app/src/com/longevitysoft/android/xml/plist/Constants.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 | 14 | package com.longevitysoft.android.xml.plist; 15 | 16 | /** 17 | * @author fbeachler 18 | * 19 | */ 20 | public class Constants { 21 | 22 | public static final java.lang.String PIPE = "|"; 23 | 24 | public static final java.lang.String TAG_PLIST = "plist"; 25 | public static final java.lang.String TAG_DICT = "dict"; 26 | public static final java.lang.String TAG_PLIST_ARRAY = "array"; 27 | public static final java.lang.String TAG_KEY = "key"; 28 | public static final java.lang.String TAG_INTEGER = "integer"; 29 | public static final java.lang.String TAG_STRING = "string"; 30 | public static final java.lang.String TAG_REAL = "real"; 31 | public static final java.lang.String TAG_DATE = "date"; 32 | public static final java.lang.String TAG_BOOL_TRUE = "true"; 33 | public static final java.lang.String TAG_BOOL_FALSE = "false"; 34 | public static final java.lang.String TAG_DATA = "data"; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /android-plist-parser-app/src/com/longevitysoft/android/xml/plist/domain/False.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 | /** 16 | * Represents a simple plist false element. 17 | */ 18 | public class False extends PListObject implements IPListSimpleObject { 19 | 20 | /** 21 | * 22 | */ 23 | private static final long serialVersionUID = -8533886020773567552L; 24 | 25 | public False() { 26 | setType(PListObjectType.FALSE); 27 | } 28 | 29 | /* 30 | * (non-Javadoc) 31 | * 32 | * @see 33 | * com.longevitysoft.android.xml.plist.domain.IPListSimpleObject#getValue() 34 | */ 35 | @Override 36 | public Boolean getValue() { 37 | return new Boolean(false); 38 | } 39 | 40 | /* 41 | * (non-Javadoc) 42 | * 43 | * @see 44 | * com.longevitysoft.android.xml.plist.domain.IPListSimpleObject#setValue 45 | * (java.lang.Object) 46 | */ 47 | @Override 48 | public void setValue(Boolean val) { 49 | // noop 50 | } 51 | 52 | /* 53 | * (non-Javadoc) 54 | * 55 | * @see 56 | * com.longevitysoft.android.xml.plist.domain.IPListSimpleObject#setValue 57 | * (java.lang.String) 58 | */ 59 | @Override 60 | public void setValue(java.lang.String val) { 61 | // noop 62 | } 63 | 64 | } -------------------------------------------------------------------------------- /android-plist-parser-app/src/com/longevitysoft/android/xml/plist/domain/Real.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 | /** 16 | * Represents a simple plist real element. 17 | */ 18 | public class Real extends PListObject implements IPListSimpleObject { 19 | 20 | // TODO: Double? 21 | protected Float real; 22 | 23 | /** 24 | * 25 | */ 26 | private static final long serialVersionUID = -4204214862534504729L; 27 | 28 | public Real() { 29 | setType(PListObjectType.REAL); 30 | } 31 | 32 | /* 33 | * (non-Javadoc) 34 | * 35 | * @see 36 | * com.longevitysoft.android.xml.plist.domain.IPListSimpleObject#getValue() 37 | */ 38 | @Override 39 | public Float getValue() { 40 | return real; 41 | } 42 | 43 | /* 44 | * (non-Javadoc) 45 | * 46 | * @see 47 | * com.longevitysoft.android.xml.plist.domain.IPListSimpleObject#setValue 48 | * (java.lang.Object) 49 | */ 50 | @Override 51 | public void setValue(Float val) { 52 | this.real = val; 53 | } 54 | 55 | /* 56 | * (non-Javadoc) 57 | * 58 | * @see 59 | * com.longevitysoft.android.xml.plist.domain.IPListSimpleObject#setValue 60 | * (java.lang.String) 61 | */ 62 | @Override 63 | public void setValue(java.lang.String val) { 64 | this.real = new Float(Float.parseFloat(val.trim())); 65 | } 66 | } -------------------------------------------------------------------------------- /android-plist-parser-app/src/com/longevitysoft/android/xml/plist/domain/Integer.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 | /** 16 | * Represents a simple plist int element. 17 | */ 18 | public class Integer extends PListObject implements 19 | IPListSimpleObject { 20 | 21 | protected java.lang.Integer intgr; 22 | 23 | /** 24 | * 25 | */ 26 | private static final long serialVersionUID = -5952071046933925529L; 27 | 28 | public Integer() { 29 | setType(PListObjectType.INTEGER); 30 | } 31 | 32 | /* 33 | * (non-Javadoc) 34 | * 35 | * @see 36 | * com.longevitysoft.android.xml.plist.domain.IPListSimpleObject#getValue() 37 | */ 38 | @Override 39 | public java.lang.Integer getValue() { 40 | return intgr; 41 | } 42 | 43 | /* 44 | * (non-Javadoc) 45 | * 46 | * @see 47 | * com.longevitysoft.android.xml.plist.domain.IPListSimpleObject#setValue 48 | * (java.lang.Object) 49 | */ 50 | @Override 51 | public void setValue(java.lang.Integer val) { 52 | this.intgr = val; 53 | } 54 | 55 | /* 56 | * (non-Javadoc) 57 | * 58 | * @see 59 | * com.longevitysoft.android.xml.plist.domain.IPListSimpleObject#setValue 60 | * (java.lang.String) 61 | */ 62 | @Override 63 | public void setValue(java.lang.String val) { 64 | this.intgr = new java.lang.Integer(java.lang.Integer.parseInt(val 65 | .trim())); 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /README.TXT: -------------------------------------------------------------------------------- 1 | Android PList parser is a SAX-based implementation of a PList-XML parser 2 | for Android. PList-XML is a format developed by Apple Inc. PLists are commonly used in Apple application development on Cocoa and iOS, and the XML format is often used in iOS applications. The intent of this parser is to fully support the PList-XML DTD (http://www.apple.com/DTDs/PropertyList-1.0.dtd). 3 | 4 | License 5 | ------- 6 | See LICENSE.TXT that accompanies this project. 7 | 8 | Project 9 | ------- 10 | The project has two modules: android-plist-parser-app and android-plist-parser-test. The Android unit tests can be run from Eclipse (you must have ADT installed) or with the Maven. 11 | 12 | If you use maven to run the tests, you'll need to install android-maven-plugin. Instructions can be found at http://code.google.com/p/maven-android-plugin/wiki/GettingStarted. The tests should run an pass via maven on the master branch. If the tests only run from Eclipse, or don't run at all, please report an issue at https://github.com/tenaciousRas/android-plist-parser/issues. 13 | 14 | Deployment 15 | ---------------- 16 | The main objective of the project is to provide consumable source code for your application. Checkout the source code and add it to your Android project, or consider using a maven build to generate a JAR for use in your application (for this, use the release profile or comment out the test module). 17 | 18 | Troubleshooting 19 | --------------------- 20 | If this parser isn't working you should consider contributing a fix back to the project. You should be able to easily follow the pattern in PListXMLParserTest.java to add test cases for your specific PList structure. You can then set debugging breakpoints and/or add logging to dig into the code and find a fix. We'd love to have a pull request from you so we can collaborate and get the code back into the mainline. 21 | 22 | TODOs 23 | ------------ 24 | - Test for Dict inside Dict. Not sure if this gets all basic valid structures per DTD but close to it. 25 | 26 | PList - Copyright 2010 Apple Inc. All Rights Reserved. -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | License 2 | ------- 3 | 4 | This work is licensed under Creative Commons and BSD licenses. See below. 5 | 6 | Android PList Parser (com.longeitysoft.android)::: 7 | Licensed under Creative Commons Attribution 3.0 Unported license. 8 | http://creativecommons.org/licenses/by/3.0/ 9 | 10 | You are free to copy, distribute and transmit the work, and to adapt the work. You must attribute android-plist-parser to Free Beachler (http://www.freebeachler.com) with link. 11 | 12 | The Android PList parser (android-plist-parser) is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 13 | 14 | 15 | Base64.java (net.sf.migbase64)::: 16 | Licensed under the BSD Open Source license. 17 | 18 | Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (base64 at miginfocom -dot- com) All rights reserved. 19 | 20 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the MiG InfoCom AB nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /android-plist-parser-app/src/com/longevitysoft/android/xml/plist/PListXMLParser.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 java.io.IOException; 16 | import java.io.InputStream; 17 | 18 | import com.longevitysoft.android.util.Stringer; 19 | 20 | /** 21 | * @author fbeachler 22 | * 23 | */ 24 | public class PListXMLParser extends BaseXMLParser { 25 | 26 | public static final String TAG = "PListXMLParser"; 27 | 28 | /** 29 | * 30 | */ 31 | public PListXMLParser() { 32 | super(); 33 | } 34 | 35 | /** 36 | * Parse a PList XML document. 37 | * 38 | * @param xml 39 | */ 40 | public void parse(String xml) throws IllegalStateException { 41 | PListXMLHandler pListHandler = (PListXMLHandler) getHandler(); 42 | if (null == pListHandler) { 43 | throw new IllegalStateException( 44 | "handler is null, must set a document handler before calling parse"); 45 | } 46 | if (null == xml) { 47 | pListHandler.setPlist(null); 48 | return; 49 | } 50 | initParser(); 51 | super.parse(xml); 52 | } 53 | 54 | /** 55 | * Parse a PList XML document from an {@link InputStream}. 56 | * 57 | * @param xml 58 | * @throws IOException 59 | */ 60 | public void parse(InputStream is) throws IllegalStateException, IOException { 61 | PListXMLHandler pListHandler = (PListXMLHandler) getHandler(); 62 | if (null == pListHandler) { 63 | throw new IllegalStateException( 64 | "handler is null, must set a document handler before calling parse"); 65 | } 66 | if (null == is) { 67 | pListHandler.setPlist(null); 68 | return; 69 | } 70 | Stringer xml = null; 71 | try { 72 | xml = Stringer.convert(is); 73 | } catch (IOException e) { 74 | throw new IOException( 75 | "error reading from input string - is it encoded as UTF-8?"); 76 | } 77 | initParser(); 78 | super.parse(xml.getBuilder().toString()); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.longevitysoft.android 6 | android-plist-parser-parent 7 | 0.9 8 | pom 9 | Android PList Parser 10 | SAX based PList parser for Android 11 | 12 | 13 | 14 | 15 | junit 16 | junit 17 | 4.8.1 18 | 19 | 20 | com.google.android 21 | android 22 | 1.6_r2 23 | provided 24 | 25 | 26 | com.google.android 27 | android-test 28 | 1.6_r2 29 | provided 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | maven-compiler-plugin 39 | 2.3 40 | 41 | 1.6 42 | 1.6 43 | UTF-8 44 | 45 | 46 | 47 | com.jayway.maven.plugins.android.generation2 48 | maven-android-plugin 49 | 2.8.3 50 | 51 | 52 | 4 53 | 54 | 55 | 56 | 57 | SDK1.6_ALL 58 | 10000 59 | -no-skin 60 | 61 | 62 | true 63 | 64 | true 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | android-plist-parser-app 73 | android-plist-parser-test 74 | 75 | 76 | -------------------------------------------------------------------------------- /android-plist-parser-app/src/com/longevitysoft/android/util/Stringer.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.util; 14 | 15 | import java.io.BufferedReader; 16 | import java.io.IOException; 17 | import java.io.InputStream; 18 | import java.io.InputStreamReader; 19 | import java.io.Reader; 20 | 21 | /** 22 | * Wrapper for {@link StringBuilder}. 23 | * 24 | * @author fbeachler 25 | * 26 | */ 27 | public class Stringer { 28 | 29 | private StringBuilder builder; 30 | 31 | /** 32 | * 33 | */ 34 | public Stringer() { 35 | builder = new StringBuilder(); 36 | } 37 | 38 | /** 39 | * 40 | */ 41 | public Stringer(String val) { 42 | builder = new StringBuilder(val); 43 | } 44 | 45 | /** 46 | * Clear the class-global stringbuilder. 47 | * 48 | * @return fluent interface to {@link builder} 49 | */ 50 | public StringBuilder newBuilder() { 51 | builder.setLength(0); 52 | return builder; 53 | } 54 | 55 | /** 56 | * Get the class-global stringbuilder. 57 | * 58 | * @return fluent interface to {@link builder} 59 | */ 60 | public StringBuilder getBuilder() { 61 | return builder; 62 | } 63 | 64 | /** 65 | * Converts an {@link InputStream} to a stringer. 66 | * 67 | * @param is 68 | * @return 69 | * @throws IOException 70 | */ 71 | public static Stringer convert(InputStream is) throws IOException { 72 | /* 73 | * To convert the InputStream to String we use the Reader.read(char[] 74 | * buffer) method. We iterate until the Reader return -1 which means 75 | * there's no more data to read. We use the Stringer class to 76 | * produce the string. 77 | */ 78 | Stringer ret = new Stringer(); 79 | if (is != null) { 80 | char[] buffer = new char[1024]; 81 | try { 82 | Reader reader = new BufferedReader(new InputStreamReader(is, 83 | "UTF-8")); 84 | int n; 85 | while ((n = reader.read(buffer)) != -1) { 86 | ret.getBuilder().append(buffer, 0, n); 87 | } 88 | } finally { 89 | is.close(); 90 | } 91 | } 92 | return ret; 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /android-plist-parser-app/src/com/longevitysoft/android/xml/plist/domain/Date.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.text.ParseException; 16 | import java.text.SimpleDateFormat; 17 | import java.util.Scanner; 18 | 19 | import android.util.Log; 20 | 21 | /** 22 | * Represents a simple plist date elements. 23 | */ 24 | public class Date extends PListObject implements 25 | IPListSimpleObject { 26 | 27 | /** 28 | * 29 | */ 30 | private static final long serialVersionUID = 3846688440069431376L; 31 | 32 | private static final java.lang.String TAG = "Date"; 33 | 34 | /** 35 | * The parsed date object. 36 | */ 37 | protected java.util.Date date; 38 | 39 | /** 40 | * Used for parsing ISO dates. 41 | */ 42 | private SimpleDateFormat iso8601Format; 43 | 44 | public Date() { 45 | setType(PListObjectType.DATE); 46 | iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); 47 | } 48 | 49 | /* 50 | * (non-Javadoc) 51 | * 52 | * @see 53 | * com.longevitysoft.android.xml.plist.domain.IPListSimpleObject#getValue() 54 | */ 55 | @Override 56 | public java.util.Date getValue() { 57 | return (java.util.Date) date; 58 | } 59 | 60 | /* 61 | * (non-Javadoc) 62 | * 63 | * @see 64 | * com.longevitysoft.android.xml.plist.domain.IPListSimpleObject#setValue 65 | * (java.lang.Object) 66 | */ 67 | @Override 68 | public void setValue(java.util.Date val) { 69 | this.date = val; 70 | } 71 | 72 | /* 73 | * (non-Javadoc) 74 | * 75 | * @see 76 | * com.longevitysoft.android.xml.plist.domain.IPListSimpleObject#setValue 77 | * (java.lang.String) 78 | */ 79 | @Override 80 | public void setValue(java.lang.String val) { 81 | // sniff date 82 | if (null == val || val.length() < 1) { 83 | this.date = null; 84 | return; 85 | } 86 | Scanner scanner = new java.util.Scanner(val).useDelimiter("-"); 87 | if (scanner.hasNextInt()) { 88 | try { 89 | this.date = iso8601Format.parse(val); 90 | } catch (ParseException e) { 91 | Log.e(TAG, new StringBuilder("#setValue - error parsing val=") 92 | .append(val).toString(), e); 93 | } 94 | } else { 95 | this.date = new java.util.Date(java.util.Date.parse(val.trim())); 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /android-plist-parser-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | com.longevitysoft.android 7 | android-plist-parser-parent 8 | 0.9 9 | 10 | 11 | com.longevitysoft.android 12 | android-plist-parser-test 13 | apk 14 | Android PList Parser Instrumentation 15 | Instrumentation for SAX based PList parser for Android 16 | 17 | 18 | 19 | GNU General Public License v3 20 | http://www.gnu.org/licenses/ 21 | 22 | 23 | 24 | 25 | 26 | bfree 27 | Free Beachler 28 | fbeachler@gmail.com 29 | 30 | developer 31 | 32 | 33 | 34 | 35 | 36 | 37 | com.google.android 38 | android 39 | provided 40 | 41 | 42 | com.google.android 43 | android-test 44 | provided 45 | 46 | 47 | com.longevitysoft.android 48 | android-plist-parser-app 49 | ${project.version} 50 | apk 51 | 52 | 53 | com.longevitysoft.android 54 | android-plist-parser-app 55 | ${project.version} 56 | provided 57 | jar 58 | 59 | 60 | 61 | 62 | ${project.artifactId} 63 | src 64 | 65 | 66 | org.apache.maven.plugins 67 | maven-compiler-plugin 68 | 69 | 1.6 70 | 1.6 71 | 72 | 73 | 74 | com.jayway.maven.plugins.android.generation2 75 | maven-android-plugin 76 | 77 | 78 | ${env.ANDROID_HOME} 79 | 1.6 80 | 81 | true 82 | 83 | true 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /android-plist-parser-app/src/com/longevitysoft/android/xml/plist/BaseXMLParser.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 java.io.IOException; 16 | import java.io.StringReader; 17 | 18 | import javax.xml.parsers.FactoryConfigurationError; 19 | import javax.xml.parsers.ParserConfigurationException; 20 | import javax.xml.parsers.SAXParser; 21 | import javax.xml.parsers.SAXParserFactory; 22 | 23 | import org.xml.sax.InputSource; 24 | import org.xml.sax.SAXException; 25 | import org.xml.sax.helpers.DefaultHandler; 26 | 27 | import android.util.Log; 28 | 29 | import com.longevitysoft.android.util.Stringer; 30 | 31 | /** 32 | * Base class for implementing SAX parsers. Provides base implementation for 33 | * initializing the parser. Subclasses can expose data retrieved by the handler, 34 | * and may want to override {@link this#parse(String)} to provide for 35 | * initializing data in the handler. 36 | * 37 | * @author fbeachler 38 | */ 39 | public abstract class BaseXMLParser { 40 | 41 | public static final String TAG = "BaseXMLParser"; 42 | 43 | /** 44 | * {@link Stringer} for this class. 45 | */ 46 | protected Stringer stringer; 47 | 48 | /** 49 | * The handler used to parse the classifications xml. 50 | */ 51 | private DefaultHandler handler; 52 | 53 | /** 54 | * Re-usable factory for gettings {@link SAXParser}s. 55 | */ 56 | protected SAXParserFactory spf; 57 | 58 | /** 59 | * Re-usable {@link SAXParser} for parsing XML. 60 | */ 61 | protected SAXParser sp; 62 | 63 | /** 64 | * Public c'tor. 65 | */ 66 | public BaseXMLParser() { 67 | stringer = new Stringer(); 68 | } 69 | 70 | /** 71 | * @return the handler 72 | */ 73 | public DefaultHandler getHandler() { 74 | return (DefaultHandler) handler; 75 | } 76 | 77 | /** 78 | * @param handler 79 | * the handler to set 80 | */ 81 | public void setHandler(DefaultHandler handler) { 82 | this.handler = handler; 83 | } 84 | 85 | /** 86 | * Creates a new {@link SAXParserFactory} and gets a new {@link SAXParser}. 87 | * 88 | * @throws FactoryConfigurationError 89 | * @throws ParserConfigurationException 90 | * @throws SAXException 91 | */ 92 | public void initParser() { 93 | // get a factory 94 | if (null == spf) { 95 | spf = SAXParserFactory.newInstance(); 96 | } 97 | // get a new parser instance 98 | try { 99 | sp = spf.newSAXParser(); 100 | } catch (ParserConfigurationException e) { 101 | Log.e( 102 | stringer.newBuilder().append(TAG).append("#parse") 103 | .toString(), "ParserConfigurationException"); 104 | e.printStackTrace(); 105 | } catch (SAXException e) { 106 | Log.e( 107 | stringer.newBuilder().append(TAG).append("#parse") 108 | .toString(), "SAXException"); 109 | e.printStackTrace(); 110 | } 111 | } 112 | 113 | /** 114 | * Parse an XML document. 115 | * 116 | * @param xml 117 | */ 118 | public void parse(String xml) throws IllegalStateException { 119 | try { 120 | // convert xml to inputsource 121 | InputSource inSrc = new InputSource(new StringReader(xml)); 122 | // register a handler for callbacks and parse the file 123 | sp.parse(inSrc, getHandler()); 124 | } catch (SAXException e) { 125 | Log.e( 126 | stringer.newBuilder().append(TAG).append("#parse") 127 | .toString(), "SAXException"); 128 | e.printStackTrace(); 129 | } catch (IOException e) { 130 | Log.e( 131 | stringer.newBuilder().append(TAG).append("#parse") 132 | .toString(), "IOException"); 133 | e.printStackTrace(); 134 | } 135 | 136 | Log.v(stringer.newBuilder().append(TAG).append("#parse").toString(), 137 | "done parsing xml"); 138 | } 139 | 140 | } -------------------------------------------------------------------------------- /android-plist-parser-app/src/com/longevitysoft/android/xml/plist/domain/Data.java: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: portions of this code (Base64 encoding/decoding) use code 3 | * licensed under the BSD agreement. 4 | * 5 | * Licensed under Creative Commons Attribution 3.0 Unported license. 6 | * http://creativecommons.org/licenses/by/3.0/ 7 | * You are free to copy, distribute and transmit the work, and 8 | * to adapt the work. You must attribute android-plist-parser 9 | * to Free Beachler (http://www.freebeachler.com). 10 | * 11 | * The Android PList parser (android-plist-parser) is distributed in 12 | * the hope that it will be useful, but WITHOUT ANY WARRANTY; without 13 | * even the implied warranty of MERCHANTABILITY or FITNESS FOR A 14 | * PARTICULAR PURPOSE. 15 | */ 16 | package com.longevitysoft.android.xml.plist.domain; 17 | 18 | import net.sf.migbase64.Base64; 19 | 20 | import com.longevitysoft.android.util.Stringer; 21 | 22 | /** 23 | * Represents a simple PList data element. The value is stored as a raw string. 24 | */ 25 | public class Data extends PListObject implements 26 | IPListSimpleObject { 27 | 28 | protected Stringer dataStringer; 29 | protected byte[] rawData; 30 | 31 | /** 32 | * 33 | */ 34 | private static final long serialVersionUID = -3101592260075687323L; 35 | 36 | public Data() { 37 | setType(PListObjectType.DATA); 38 | dataStringer = new Stringer(); 39 | } 40 | 41 | /** 42 | * Get the raw Base64 encoded data value on this object. Assumes the data 43 | * has already been encoded. 44 | * 45 | * @see com.longevitysoft.android.xml.plist.PListXMLHandler.PList. 46 | * IPListSimpleObject#getValue() 47 | */ 48 | @Override 49 | public java.lang.String getValue() { 50 | return getValue(true); 51 | } 52 | 53 | /** 54 | * Get the raw Base64 encoded data value on this object. Assumes the data 55 | * has already been encoded. 56 | * 57 | * @param decode 58 | * - if true, the result is Base64 decoded before being returned. 59 | * NOTE: this calls {@link Base64#decodeFast(byte[])} which 60 | * expects the raw encoded data to be on one line (no line 61 | * separators). To change this behavior, subclass and override. 62 | * 63 | * @see com.longevitysoft.android.xml.plist.PListXMLHandler.PList. 64 | * IPListSimpleObject#getValue() 65 | */ 66 | public java.lang.String getValue(boolean decode) { 67 | dataStringer.newBuilder(); 68 | if (decode) { 69 | return dataStringer.getBuilder() 70 | .append(new java.lang.String(Base64.decodeFast(rawData))) 71 | .toString(); 72 | } else { 73 | return dataStringer.getBuilder().append(rawData).toString(); 74 | } 75 | } 76 | 77 | /** 78 | * Sets the raw Base64 data value on this object. Assumes the data is 79 | * properly encoded. 80 | * 81 | * @param val 82 | * - Base64 encoded data to set 83 | * 84 | * @see com.longevitysoft.android.xml.plist.PListXMLHandler.PList. 85 | * IPListSimpleObject#setValue(java.lang.Object) 86 | */ 87 | @Override 88 | public void setValue(java.lang.String val) { 89 | setValue(val, true); 90 | } 91 | 92 | /** 93 | * Sets the data value on this object. 94 | * 95 | * @param val 96 | * - unencoded data to set 97 | * @param encoded 98 | * - flag true if val is Base64 encoded already. If false, val is 99 | * encoded (without line seperators) before being stored. 100 | * 101 | * @see com.longevitysoft.android.xml.plist.PListXMLHandler.PList. 102 | * IPListSimpleObject#setValue(java.lang.Object) 103 | */ 104 | public void setValue(java.lang.String val, boolean encoded) { 105 | if (!encoded) { 106 | rawData = Base64.encodeToByte(val.getBytes(), false); 107 | } else { 108 | rawData = val.getBytes(); 109 | } 110 | } 111 | 112 | /** 113 | * Sets the data value on this object. 114 | * 115 | * @param val 116 | * - unencoded data to set 117 | * @param encoded 118 | * - flag true if val is Base64 encoded already. If false, val is 119 | * encoded (without line seperators) before being stored. 120 | * 121 | * @see com.longevitysoft.android.xml.plist.PListXMLHandler.PList. 122 | * IPListSimpleObject#setValue(java.lang.Object) 123 | */ 124 | public void setValue(byte[] val, boolean encoded) { 125 | if (!encoded) { 126 | rawData = Base64.encodeToByte(val, false); 127 | } else { 128 | rawData = val; 129 | } 130 | } 131 | 132 | } -------------------------------------------------------------------------------- /android-plist-parser-app/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | com.longevitysoft.android 8 | android-plist-parser-parent 9 | 0.9 10 | 11 | 12 | com.longevitysoft.android 13 | android-plist-parser-app 14 | apk 15 | Android PList Parser Source/App 16 | Stub App to wrap SAX based PList parser for Android 17 | 18 | 19 | 20 | GNU General Public License v3 21 | http://www.gnu.org/licenses/ 22 | 23 | 24 | 25 | 26 | 27 | bfree 28 | Free Beachler 29 | fbeachler@gmail.com 30 | 31 | developer 32 | 33 | 34 | 35 | 36 | 37 | 38 | junit 39 | junit 40 | test 41 | 42 | 43 | com.google.android 44 | android 45 | provided 46 | 47 | 48 | 49 | 50 | ${project.artifactId} 51 | src 52 | 53 | 54 | org.apache.maven.plugins 55 | maven-compiler-plugin 56 | 57 | 1.6 58 | 1.6 59 | 60 | 61 | 62 | com.jayway.maven.plugins.android.generation2 63 | maven-android-plugin 64 | true 65 | 66 | 67 | startEmulator 68 | initialize 69 | 70 | emulator-start 71 | 72 | 73 | 74 | 75 | 76 | org.codehaus.mojo 77 | build-helper-maven-plugin 78 | 79 | 80 | parse-version 81 | 82 | parse-version 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 96 | 97 | linux 98 | 99 | 100 | unix 101 | 102 | 103 | 104 | ${env.JAVA_HOME}/jre/lib/rt.jar 105 | ${env.JAVA_HOME}/jre/lib/jsse.jar 106 | 107 | 108 | 112 | 113 | mac 114 | 115 | 116 | mac 117 | 118 | 119 | 120 | 121 | /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Classes/classes.jar 122 | /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Classes/jsse.jar 123 | 129 | 130 | 131 | 132 | windows 133 | 134 | 135 | windows 136 | 137 | 138 | 139 | ${env.JAVA_HOME}/jre/lib/rt.jar 140 | ${env.JAVA_HOME}/jre/lib/jsse.jar 141 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /android-plist-parser-app/src/com/longevitysoft/android/xml/plist/domain/Dict.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.Iterator; 16 | import java.util.Map; 17 | import java.util.Set; 18 | import java.util.StringTokenizer; 19 | import java.util.TreeMap; 20 | 21 | /** 22 | * Represents a PList Dict object. 23 | * 24 | * @author fbeachler 25 | */ 26 | public class Dict extends PListObject { 27 | 28 | /** 29 | * 30 | */ 31 | private static final long serialVersionUID = -556589348083152733L; 32 | 33 | public static final java.lang.String DOT = "."; 34 | protected Map configMap; 35 | 36 | public Dict() { 37 | configMap = new TreeMap(); 38 | setType(PListObjectType.DICT); 39 | } 40 | 41 | /** 42 | * Put the config value with the given key. 43 | * 44 | * @param key 45 | * @param value 46 | */ 47 | public void putConfig(java.lang.String key, PListObject value) { 48 | configMap.put(key, value); 49 | } 50 | 51 | /* 52 | * (non-Javadoc) 53 | * 54 | * @see java.lang.Object#toString() 55 | */ 56 | /** 57 | * @return the configMap 58 | */ 59 | public Map getConfigMap() { 60 | return configMap; 61 | } 62 | 63 | /** 64 | * @param configMap 65 | * the configMap to set 66 | */ 67 | public void setConfigMap(Map configMap) { 68 | this.configMap = configMap; 69 | } 70 | 71 | /** 72 | * Utility method which tokenizes the given keyName using the "." delimiter 73 | * and then looks up each token in the configuration dictionary. If the 74 | * token key points to a dictionary then it proceeds to the next token key 75 | * and looks up value of the token key in the dictionary it found from the 76 | * previous token key. 77 | * 78 | * @param key 79 | * The fully qualified key text. 80 | * @return The Object value associated with the given key, or null if the 81 | * key does not exist. 82 | */ 83 | @SuppressWarnings("unchecked") 84 | public E getConfigurationObject(java.lang.String key) { 85 | StringTokenizer st = new StringTokenizer(key, DOT); 86 | 87 | if (st.hasMoreTokens()) { 88 | Map dict = configMap; 89 | Object obj; 90 | while (st.hasMoreTokens()) { 91 | java.lang.String token = st.nextToken(); 92 | obj = dict.get(token); 93 | if (obj instanceof Dict) { 94 | dict = ((Dict) obj).getConfigMap(); 95 | continue; 96 | } 97 | return (E) obj; 98 | } 99 | } 100 | return (E) configMap.get(key); 101 | } 102 | 103 | /** 104 | * Get an String configuration value for the given key. 105 | * 106 | * @param key 107 | * The text of the key to look up in the configuration 108 | * dictionary. 109 | * @return The String value of the specified key. 110 | */ 111 | public String getConfiguration(java.lang.String key) { 112 | return (String) getConfigurationObject(key); 113 | } 114 | 115 | /** 116 | * Get a String configuration value for the given key. If there is no value 117 | * for the given key, then return the default value. 118 | * 119 | * @param key 120 | * The text of the key to look up in the configuration 121 | * dictionary. 122 | * @param defaultValue 123 | * The default value to return if they key has no associated 124 | * value. 125 | * @return The String value of the specified key, or defaultValue if the 126 | * value for keyName is null. 127 | */ 128 | public String getConfigurationWithDefault(java.lang.String key, 129 | String defaultValue) { 130 | String value = getConfiguration(key); 131 | if (value == null) { 132 | return defaultValue; 133 | } 134 | 135 | return value; 136 | } 137 | 138 | /** 139 | * Get an Integer configuration value for the given key. 140 | * 141 | * @param key 142 | * The text of the key to look up in the configuration 143 | * dictionary. 144 | * @return The Integer value of the specified key. 145 | */ 146 | public Integer getConfigurationInteger(java.lang.String key) { 147 | return (Integer) getConfigurationObject(key); 148 | } 149 | 150 | /** 151 | * Get an Integer configuration value for the given key. If there is no 152 | * value for the given key, then return the default value. 153 | * 154 | * @param key 155 | * The text of the key to look up in the configuration 156 | * dictionary. 157 | * @param defaultValue 158 | * The default value to return if they key has no associated 159 | * value. 160 | * @return The Integer value of the specified key, or defaultValue if the 161 | * value for keyName is null. 162 | */ 163 | public Integer getConfigurationIntegerWithDefault(java.lang.String key, 164 | Integer defaultValue) { 165 | Integer value = getConfigurationInteger(key); 166 | if (value == null) { 167 | return defaultValue; 168 | } 169 | 170 | return value; 171 | } 172 | 173 | /** 174 | * Get an Integer configuration value for the given key. 175 | * 176 | * @param key 177 | * The text of the key to look up in the configuration 178 | * dictionary. 179 | * @return The Integer value of the specified key. 180 | */ 181 | public Array getConfigurationArray(java.lang.String key) { 182 | return (Array) getConfigurationObject(key); 183 | } 184 | 185 | /* 186 | * (non-Javadoc) 187 | * 188 | * @see java.lang.Object#toString() 189 | */ 190 | @Override 191 | public java.lang.String toString() { 192 | StringBuilder retVal = new StringBuilder(); 193 | Set keys = configMap.keySet(); 194 | Iterator it = keys.iterator(); 195 | while (it.hasNext()) { 196 | java.lang.String key = it.next(); 197 | retVal.append("key=").append(key) 198 | .append(configMap.get(key).toString()); 199 | } 200 | return retVal.toString(); 201 | } 202 | 203 | } -------------------------------------------------------------------------------- /android-plist-parser-app/src/com/longevitysoft/android/xml/plist/domain/Array.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.ArrayList; 16 | import java.util.Collection; 17 | import java.util.Iterator; 18 | import java.util.List; 19 | import java.util.ListIterator; 20 | 21 | /** 22 | * Represents a PList Array object. Essentially a proxy for a 23 | * {@link java.util.List} implementation that contains a list of 24 | * {@link PListObject}s. 25 | * 26 | * @author fbeachler 27 | * 28 | */ 29 | public class Array extends PListObject implements java.util.List { 30 | 31 | private ArrayList data; 32 | 33 | /** 34 | * 35 | */ 36 | private static final long serialVersionUID = -2673110114913406413L; 37 | 38 | /** 39 | * 40 | */ 41 | public Array() { 42 | setType(PListObjectType.ARRAY); 43 | data = new ArrayList(); 44 | } 45 | 46 | /** 47 | * @param collection 48 | */ 49 | public Array(Collection collection) { 50 | setType(PListObjectType.ARRAY); 51 | data = new ArrayList(collection); 52 | } 53 | 54 | /** 55 | * @param capacity 56 | */ 57 | public Array(int capacity) { 58 | setType(PListObjectType.ARRAY); 59 | data = new ArrayList(capacity); 60 | } 61 | 62 | /* 63 | * (non-Javadoc) 64 | * 65 | * @see java.util.List#add(int, java.lang.Object) 66 | */ 67 | @Override 68 | public void add(int arg0, PListObject arg1) { 69 | data.add(arg0, (PListObject) arg1); 70 | } 71 | 72 | /* 73 | * (non-Javadoc) 74 | * 75 | * @see java.util.List#add(java.lang.Object) 76 | */ 77 | @Override 78 | public boolean add(PListObject arg0) { 79 | return data.add((PListObject) arg0); 80 | } 81 | 82 | /* 83 | * (non-Javadoc) 84 | * 85 | * @see java.util.List#addAll(java.util.Collection) 86 | */ 87 | @Override 88 | public boolean addAll(Collection arg0) { 89 | return data.addAll(arg0); 90 | } 91 | 92 | /* 93 | * (non-Javadoc) 94 | * 95 | * @see java.util.List#addAll(int, java.util.Collection) 96 | */ 97 | @Override 98 | public boolean addAll(int arg0, Collection arg1) { 99 | return data.addAll(arg0, arg1); 100 | } 101 | 102 | /* 103 | * (non-Javadoc) 104 | * 105 | * @see java.util.List#isEmpty() 106 | */ 107 | @Override 108 | public boolean isEmpty() { 109 | return data.isEmpty(); 110 | } 111 | 112 | /* 113 | * (non-Javadoc) 114 | * 115 | * @see java.util.List#lastIndexOf(java.lang.Object) 116 | */ 117 | @Override 118 | public int lastIndexOf(Object arg0) { 119 | return data.indexOf(arg0); 120 | } 121 | 122 | /* 123 | * (non-Javadoc) 124 | * 125 | * @see java.util.List#listIterator() 126 | */ 127 | @Override 128 | public ListIterator listIterator() { 129 | return data.listIterator(); 130 | } 131 | 132 | /* 133 | * (non-Javadoc) 134 | * 135 | * @see java.util.List#listIterator(int) 136 | */ 137 | @Override 138 | public ListIterator listIterator(int arg0) { 139 | return data.listIterator(arg0); 140 | } 141 | 142 | /* 143 | * (non-Javadoc) 144 | * 145 | * @see java.util.List#remove(int) 146 | */ 147 | @Override 148 | public PListObject remove(int arg0) { 149 | return data.remove(arg0); 150 | } 151 | 152 | /* 153 | * (non-Javadoc) 154 | * 155 | * @see java.util.List#remove(java.lang.Object) 156 | */ 157 | @Override 158 | public boolean remove(Object arg0) { 159 | return data.remove(arg0); 160 | } 161 | 162 | /* 163 | * (non-Javadoc) 164 | * 165 | * @see java.util.List#removeAll(java.util.Collection) 166 | */ 167 | @Override 168 | public boolean removeAll(Collection arg0) { 169 | return data.remove(arg0); 170 | } 171 | 172 | /* 173 | * (non-Javadoc) 174 | * 175 | * @see java.util.List#retainAll(java.util.Collection) 176 | */ 177 | @Override 178 | public boolean retainAll(Collection arg0) { 179 | return data.retainAll(arg0); 180 | } 181 | 182 | /* 183 | * (non-Javadoc) 184 | * 185 | * @see java.util.List#set(int, java.lang.Object) 186 | */ 187 | @Override 188 | public PListObject set(int arg0, PListObject arg1) { 189 | return data.set(arg0, arg1); 190 | } 191 | 192 | /* 193 | * (non-Javadoc) 194 | * 195 | * @see java.util.List#subList(int, int) 196 | */ 197 | @Override 198 | public List subList(int arg0, int arg1) { 199 | return data.subList(arg0, arg1); 200 | } 201 | 202 | /* 203 | * (non-Javadoc) 204 | * 205 | * @see java.util.List#toArray() 206 | */ 207 | @Override 208 | public Object[] toArray() { 209 | return data.toArray(); 210 | } 211 | 212 | /* 213 | * (non-Javadoc) 214 | * 215 | * @see java.util.List#toArray(T[]) 216 | */ 217 | @SuppressWarnings("unchecked") 218 | @Override 219 | public Object[] toArray(Object[] array) { 220 | return data.toArray(array); 221 | } 222 | 223 | /** 224 | * @see {@link java.util.ArrayList#clear()} 225 | */ 226 | public void clear() { 227 | data.clear(); 228 | } 229 | 230 | /** 231 | * @see {@link java.util.ArrayList#clone()} 232 | */ 233 | public Object clone() { 234 | return data.clone(); 235 | } 236 | 237 | /** 238 | * @see {@link java.util.ArrayList#contains(Object)} 239 | */ 240 | public boolean contains(Object obj) { 241 | return data.contains(obj); 242 | } 243 | 244 | /* 245 | * (non-Javadoc) 246 | * 247 | * @see java.util.List#containsAll(java.util.Collection) 248 | */ 249 | @Override 250 | public boolean containsAll(@SuppressWarnings("rawtypes") Collection arg0) { 251 | return data.contains(arg0); 252 | } 253 | 254 | /** 255 | * @see {@link java.util.ArrayList#equals(Object)} 256 | */ 257 | public boolean equals(Object that) { 258 | return data.equals(that); 259 | } 260 | 261 | /** 262 | * @see {@link java.util.ArrayList#get(int)} 263 | */ 264 | public PListObject get(int index) { 265 | return data.get(index); 266 | } 267 | 268 | /** 269 | * @see {@link java.util.ArrayList#indexOf(Object)} 270 | */ 271 | public int indexOf(Object object) { 272 | return data.indexOf(object); 273 | } 274 | 275 | /** 276 | * @see {@link java.util.ArrayList#iterator()} 277 | */ 278 | public Iterator 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= 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= 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 | } --------------------------------------------------------------------------------