From b32e5a83968cf7c966f07b762515a39429537c9a Mon Sep 17 00:00:00 2001 From: Vivek Bhagwat Date: Wed, 1 Feb 2012 22:53:09 -0500 Subject: [PATCH] First commit to new iPhone-only repo --- .../project.pbxproj | 402 ++++++++++++++++ .../contents.xcworkspacedata | 7 + .../UserInterfaceState.xcuserstate | Bin 0 -> 27599 bytes .../xcschemes/CardsAgainstHumanity.xcscheme | 84 ++++ .../xcschemes/xcschememanagement.plist | 22 + CardsAgainstHumanity/AppDelegate.h | 20 + CardsAgainstHumanity/AppDelegate.m | 86 ++++ .../BlackCardsViewController.h | 18 + .../BlackCardsViewController.m | 65 +++ .../BlackCardsViewController.xib | 161 +++++++ .../CardsAgainstHumanity-Info.plist | 40 ++ .../CardsAgainstHumanity-Prefix.pch | 14 + CardsAgainstHumanity/CardsViewController.h | 22 + CardsAgainstHumanity/CardsViewController.m | 73 +++ CardsAgainstHumanity/CardsViewController.xib | 237 +++++++++ CardsAgainstHumanity/LoginViewController.h | 23 + CardsAgainstHumanity/LoginViewController.m | 129 +++++ CardsAgainstHumanity/LoginViewController.xib | 295 ++++++++++++ CardsAgainstHumanity/Room.h | 13 + CardsAgainstHumanity/Room.m | 13 + CardsAgainstHumanity/RoomViewController.h | 20 + CardsAgainstHumanity/RoomViewController.m | 239 +++++++++ CardsAgainstHumanity/RoomViewController.xib | 186 +++++++ .../WhiteCardsViewController.h | 17 + .../WhiteCardsViewController.m | 70 +++ .../WhiteCardsViewController.xib | 166 +++++++ .../en.lproj/InfoPlist.strings | 2 + CardsAgainstHumanity/main.m | 18 + NSObject+SBJson.h | 79 +++ NSObject+SBJson.m | 72 +++ SBJson.h | 84 ++++ SBJsonParser.h | 101 ++++ SBJsonParser.m | 100 ++++ SBJsonStreamParser.h | 161 +++++++ SBJsonStreamParser.m | 255 ++++++++++ SBJsonStreamParserAccumulator.h | 37 ++ SBJsonStreamParserAccumulator.m | 47 ++ SBJsonStreamParserAdapter.h | 148 ++++++ SBJsonStreamParserAdapter.m | 164 +++++++ SBJsonStreamParserState.h | 83 ++++ SBJsonStreamParserState.m | 355 ++++++++++++++ SBJsonStreamWriter.h | 188 ++++++++ SBJsonStreamWriter.m | 366 ++++++++++++++ SBJsonStreamWriterAccumulator.h | 36 ++ SBJsonStreamWriterAccumulator.m | 52 ++ SBJsonStreamWriterState.h | 69 +++ SBJsonStreamWriterState.m | 139 ++++++ SBJsonTokeniser.h | 67 +++ SBJsonTokeniser.m | 453 ++++++++++++++++++ SBJsonUTF8Stream.h | 58 +++ SBJsonUTF8Stream.m | 141 ++++++ SBJsonWriter.h | 110 +++++ SBJsonWriter.m | 109 +++++ 53 files changed, 5916 insertions(+) create mode 100644 CardsAgainstHumanity.xcodeproj/project.pbxproj create mode 100644 CardsAgainstHumanity.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 CardsAgainstHumanity.xcodeproj/project.xcworkspace/xcuserdata/vivek.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 CardsAgainstHumanity.xcodeproj/xcuserdata/vivek.xcuserdatad/xcschemes/CardsAgainstHumanity.xcscheme create mode 100644 CardsAgainstHumanity.xcodeproj/xcuserdata/vivek.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 CardsAgainstHumanity/AppDelegate.h create mode 100644 CardsAgainstHumanity/AppDelegate.m create mode 100644 CardsAgainstHumanity/BlackCardsViewController.h create mode 100644 CardsAgainstHumanity/BlackCardsViewController.m create mode 100644 CardsAgainstHumanity/BlackCardsViewController.xib create mode 100644 CardsAgainstHumanity/CardsAgainstHumanity-Info.plist create mode 100644 CardsAgainstHumanity/CardsAgainstHumanity-Prefix.pch create mode 100644 CardsAgainstHumanity/CardsViewController.h create mode 100644 CardsAgainstHumanity/CardsViewController.m create mode 100644 CardsAgainstHumanity/CardsViewController.xib create mode 100644 CardsAgainstHumanity/LoginViewController.h create mode 100644 CardsAgainstHumanity/LoginViewController.m create mode 100644 CardsAgainstHumanity/LoginViewController.xib create mode 100644 CardsAgainstHumanity/Room.h create mode 100644 CardsAgainstHumanity/Room.m create mode 100644 CardsAgainstHumanity/RoomViewController.h create mode 100644 CardsAgainstHumanity/RoomViewController.m create mode 100644 CardsAgainstHumanity/RoomViewController.xib create mode 100644 CardsAgainstHumanity/WhiteCardsViewController.h create mode 100644 CardsAgainstHumanity/WhiteCardsViewController.m create mode 100644 CardsAgainstHumanity/WhiteCardsViewController.xib create mode 100644 CardsAgainstHumanity/en.lproj/InfoPlist.strings create mode 100644 CardsAgainstHumanity/main.m create mode 100755 NSObject+SBJson.h create mode 100755 NSObject+SBJson.m create mode 100755 SBJson.h create mode 100755 SBJsonParser.h create mode 100755 SBJsonParser.m create mode 100755 SBJsonStreamParser.h create mode 100755 SBJsonStreamParser.m create mode 100755 SBJsonStreamParserAccumulator.h create mode 100755 SBJsonStreamParserAccumulator.m create mode 100755 SBJsonStreamParserAdapter.h create mode 100755 SBJsonStreamParserAdapter.m create mode 100755 SBJsonStreamParserState.h create mode 100755 SBJsonStreamParserState.m create mode 100755 SBJsonStreamWriter.h create mode 100755 SBJsonStreamWriter.m create mode 100755 SBJsonStreamWriterAccumulator.h create mode 100755 SBJsonStreamWriterAccumulator.m create mode 100755 SBJsonStreamWriterState.h create mode 100755 SBJsonStreamWriterState.m create mode 100755 SBJsonTokeniser.h create mode 100755 SBJsonTokeniser.m create mode 100755 SBJsonUTF8Stream.h create mode 100755 SBJsonUTF8Stream.m create mode 100755 SBJsonWriter.h create mode 100755 SBJsonWriter.m diff --git a/CardsAgainstHumanity.xcodeproj/project.pbxproj b/CardsAgainstHumanity.xcodeproj/project.pbxproj new file mode 100644 index 0000000..cf458f5 --- /dev/null +++ b/CardsAgainstHumanity.xcodeproj/project.pbxproj @@ -0,0 +1,402 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + E296ACA814D526C1006F96D2 /* BlackCardsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E296ACA614D526C1006F96D2 /* BlackCardsViewController.m */; }; + E296ACA914D526C1006F96D2 /* BlackCardsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E296ACA714D526C1006F96D2 /* BlackCardsViewController.xib */; }; + E296ACB214D5B750006F96D2 /* CardsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E296ACB014D5B750006F96D2 /* CardsViewController.m */; }; + E296ACB314D5B750006F96D2 /* CardsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E296ACB114D5B750006F96D2 /* CardsViewController.xib */; }; + E296AD1414D5D042006F96D2 /* LoginViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E296AD1214D5D041006F96D2 /* LoginViewController.m */; }; + E296AD1514D5D042006F96D2 /* LoginViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E296AD1314D5D042006F96D2 /* LoginViewController.xib */; }; + E296AD3314D5DDBA006F96D2 /* NSObject+SBJson.m in Sources */ = {isa = PBXBuildFile; fileRef = E296AD1B14D5DDBA006F96D2 /* NSObject+SBJson.m */; }; + E296AD3414D5DDBA006F96D2 /* SBJsonParser.m in Sources */ = {isa = PBXBuildFile; fileRef = E296AD1E14D5DDBA006F96D2 /* SBJsonParser.m */; }; + E296AD3514D5DDBA006F96D2 /* SBJsonStreamParser.m in Sources */ = {isa = PBXBuildFile; fileRef = E296AD2014D5DDBA006F96D2 /* SBJsonStreamParser.m */; }; + E296AD3614D5DDBA006F96D2 /* SBJsonStreamParserAccumulator.m in Sources */ = {isa = PBXBuildFile; fileRef = E296AD2214D5DDBA006F96D2 /* SBJsonStreamParserAccumulator.m */; }; + E296AD3714D5DDBA006F96D2 /* SBJsonStreamParserAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = E296AD2414D5DDBA006F96D2 /* SBJsonStreamParserAdapter.m */; }; + E296AD3814D5DDBA006F96D2 /* SBJsonStreamParserState.m in Sources */ = {isa = PBXBuildFile; fileRef = E296AD2614D5DDBA006F96D2 /* SBJsonStreamParserState.m */; }; + E296AD3914D5DDBA006F96D2 /* SBJsonStreamWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = E296AD2814D5DDBA006F96D2 /* SBJsonStreamWriter.m */; }; + E296AD3A14D5DDBA006F96D2 /* SBJsonStreamWriterAccumulator.m in Sources */ = {isa = PBXBuildFile; fileRef = E296AD2A14D5DDBA006F96D2 /* SBJsonStreamWriterAccumulator.m */; }; + E296AD3B14D5DDBA006F96D2 /* SBJsonStreamWriterState.m in Sources */ = {isa = PBXBuildFile; fileRef = E296AD2C14D5DDBA006F96D2 /* SBJsonStreamWriterState.m */; }; + E296AD3C14D5DDBA006F96D2 /* SBJsonTokeniser.m in Sources */ = {isa = PBXBuildFile; fileRef = E296AD2E14D5DDBA006F96D2 /* SBJsonTokeniser.m */; }; + E296AD3D14D5DDBA006F96D2 /* SBJsonUTF8Stream.m in Sources */ = {isa = PBXBuildFile; fileRef = E296AD3014D5DDBA006F96D2 /* SBJsonUTF8Stream.m */; }; + E296AD3E14D5DDBA006F96D2 /* SBJsonWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = E296AD3214D5DDBA006F96D2 /* SBJsonWriter.m */; }; + E299158F14D4E63100C5B7F3 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E299158E14D4E63100C5B7F3 /* UIKit.framework */; }; + E299159114D4E63100C5B7F3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E299159014D4E63100C5B7F3 /* Foundation.framework */; }; + E299159314D4E63100C5B7F3 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E299159214D4E63100C5B7F3 /* CoreGraphics.framework */; }; + E299159914D4E63100C5B7F3 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = E299159714D4E63100C5B7F3 /* InfoPlist.strings */; }; + E299159B14D4E63100C5B7F3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E299159A14D4E63100C5B7F3 /* main.m */; }; + E299159F14D4E63100C5B7F3 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E299159E14D4E63100C5B7F3 /* AppDelegate.m */; }; + E29915A814D4E66D00C5B7F3 /* RoomViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E29915A614D4E66D00C5B7F3 /* RoomViewController.m */; }; + E29915A914D4E66D00C5B7F3 /* RoomViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E29915A714D4E66D00C5B7F3 /* RoomViewController.xib */; }; + E29915AF14D4E69000C5B7F3 /* Room.m in Sources */ = {isa = PBXBuildFile; fileRef = E29915AE14D4E69000C5B7F3 /* Room.m */; }; + E29915B314D4EA1800C5B7F3 /* WhiteCardsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E29915B114D4EA1800C5B7F3 /* WhiteCardsViewController.m */; }; + E29915B414D4EA1800C5B7F3 /* WhiteCardsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E29915B214D4EA1800C5B7F3 /* WhiteCardsViewController.xib */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + E296ACA514D526C1006F96D2 /* BlackCardsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlackCardsViewController.h; sourceTree = ""; }; + E296ACA614D526C1006F96D2 /* BlackCardsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BlackCardsViewController.m; sourceTree = ""; }; + E296ACA714D526C1006F96D2 /* BlackCardsViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BlackCardsViewController.xib; sourceTree = ""; }; + E296ACAF14D5B750006F96D2 /* CardsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CardsViewController.h; sourceTree = ""; }; + E296ACB014D5B750006F96D2 /* CardsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CardsViewController.m; sourceTree = ""; }; + E296ACB114D5B750006F96D2 /* CardsViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CardsViewController.xib; sourceTree = ""; }; + E296AD1114D5D040006F96D2 /* LoginViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoginViewController.h; sourceTree = ""; }; + E296AD1214D5D041006F96D2 /* LoginViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LoginViewController.m; sourceTree = ""; }; + E296AD1314D5D042006F96D2 /* LoginViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LoginViewController.xib; sourceTree = ""; }; + E296AD1A14D5DDBA006F96D2 /* NSObject+SBJson.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+SBJson.h"; sourceTree = ""; }; + E296AD1B14D5DDBA006F96D2 /* NSObject+SBJson.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+SBJson.m"; sourceTree = ""; }; + E296AD1C14D5DDBA006F96D2 /* SBJson.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJson.h; sourceTree = ""; }; + E296AD1D14D5DDBA006F96D2 /* SBJsonParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonParser.h; sourceTree = ""; }; + E296AD1E14D5DDBA006F96D2 /* SBJsonParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonParser.m; sourceTree = ""; }; + E296AD1F14D5DDBA006F96D2 /* SBJsonStreamParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamParser.h; sourceTree = ""; }; + E296AD2014D5DDBA006F96D2 /* SBJsonStreamParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamParser.m; sourceTree = ""; }; + E296AD2114D5DDBA006F96D2 /* SBJsonStreamParserAccumulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamParserAccumulator.h; sourceTree = ""; }; + E296AD2214D5DDBA006F96D2 /* SBJsonStreamParserAccumulator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamParserAccumulator.m; sourceTree = ""; }; + E296AD2314D5DDBA006F96D2 /* SBJsonStreamParserAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamParserAdapter.h; sourceTree = ""; }; + E296AD2414D5DDBA006F96D2 /* SBJsonStreamParserAdapter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamParserAdapter.m; sourceTree = ""; }; + E296AD2514D5DDBA006F96D2 /* SBJsonStreamParserState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamParserState.h; sourceTree = ""; }; + E296AD2614D5DDBA006F96D2 /* SBJsonStreamParserState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamParserState.m; sourceTree = ""; }; + E296AD2714D5DDBA006F96D2 /* SBJsonStreamWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamWriter.h; sourceTree = ""; }; + E296AD2814D5DDBA006F96D2 /* SBJsonStreamWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamWriter.m; sourceTree = ""; }; + E296AD2914D5DDBA006F96D2 /* SBJsonStreamWriterAccumulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamWriterAccumulator.h; sourceTree = ""; }; + E296AD2A14D5DDBA006F96D2 /* SBJsonStreamWriterAccumulator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamWriterAccumulator.m; sourceTree = ""; }; + E296AD2B14D5DDBA006F96D2 /* SBJsonStreamWriterState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamWriterState.h; sourceTree = ""; }; + E296AD2C14D5DDBA006F96D2 /* SBJsonStreamWriterState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamWriterState.m; sourceTree = ""; }; + E296AD2D14D5DDBA006F96D2 /* SBJsonTokeniser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonTokeniser.h; sourceTree = ""; }; + E296AD2E14D5DDBA006F96D2 /* SBJsonTokeniser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonTokeniser.m; sourceTree = ""; }; + E296AD2F14D5DDBA006F96D2 /* SBJsonUTF8Stream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonUTF8Stream.h; sourceTree = ""; }; + E296AD3014D5DDBA006F96D2 /* SBJsonUTF8Stream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonUTF8Stream.m; sourceTree = ""; }; + E296AD3114D5DDBA006F96D2 /* SBJsonWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonWriter.h; sourceTree = ""; }; + E296AD3214D5DDBA006F96D2 /* SBJsonWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonWriter.m; sourceTree = ""; }; + E299158A14D4E63100C5B7F3 /* CardsAgainstHumanity.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CardsAgainstHumanity.app; sourceTree = BUILT_PRODUCTS_DIR; }; + E299158E14D4E63100C5B7F3 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + E299159014D4E63100C5B7F3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + E299159214D4E63100C5B7F3 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + E299159614D4E63100C5B7F3 /* CardsAgainstHumanity-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CardsAgainstHumanity-Info.plist"; sourceTree = ""; }; + E299159814D4E63100C5B7F3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + E299159A14D4E63100C5B7F3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + E299159C14D4E63100C5B7F3 /* CardsAgainstHumanity-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CardsAgainstHumanity-Prefix.pch"; sourceTree = ""; }; + E299159D14D4E63100C5B7F3 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + E299159E14D4E63100C5B7F3 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + E29915A514D4E66D00C5B7F3 /* RoomViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RoomViewController.h; sourceTree = ""; }; + E29915A614D4E66D00C5B7F3 /* RoomViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RoomViewController.m; sourceTree = ""; }; + E29915A714D4E66D00C5B7F3 /* RoomViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RoomViewController.xib; sourceTree = ""; }; + E29915AD14D4E69000C5B7F3 /* Room.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Room.h; sourceTree = ""; }; + E29915AE14D4E69000C5B7F3 /* Room.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Room.m; sourceTree = ""; }; + E29915B014D4EA1800C5B7F3 /* WhiteCardsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WhiteCardsViewController.h; sourceTree = ""; }; + E29915B114D4EA1800C5B7F3 /* WhiteCardsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WhiteCardsViewController.m; sourceTree = ""; }; + E29915B214D4EA1800C5B7F3 /* WhiteCardsViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WhiteCardsViewController.xib; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + E299158714D4E63100C5B7F3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E299158F14D4E63100C5B7F3 /* UIKit.framework in Frameworks */, + E299159114D4E63100C5B7F3 /* Foundation.framework in Frameworks */, + E299159314D4E63100C5B7F3 /* CoreGraphics.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + E296AD1914D5DD9A006F96D2 /* JSON */ = { + isa = PBXGroup; + children = ( + E296AD1A14D5DDBA006F96D2 /* NSObject+SBJson.h */, + E296AD1B14D5DDBA006F96D2 /* NSObject+SBJson.m */, + E296AD1C14D5DDBA006F96D2 /* SBJson.h */, + E296AD1D14D5DDBA006F96D2 /* SBJsonParser.h */, + E296AD1E14D5DDBA006F96D2 /* SBJsonParser.m */, + E296AD1F14D5DDBA006F96D2 /* SBJsonStreamParser.h */, + E296AD2014D5DDBA006F96D2 /* SBJsonStreamParser.m */, + E296AD2114D5DDBA006F96D2 /* SBJsonStreamParserAccumulator.h */, + E296AD2214D5DDBA006F96D2 /* SBJsonStreamParserAccumulator.m */, + E296AD2314D5DDBA006F96D2 /* SBJsonStreamParserAdapter.h */, + E296AD2414D5DDBA006F96D2 /* SBJsonStreamParserAdapter.m */, + E296AD2514D5DDBA006F96D2 /* SBJsonStreamParserState.h */, + E296AD2614D5DDBA006F96D2 /* SBJsonStreamParserState.m */, + E296AD2714D5DDBA006F96D2 /* SBJsonStreamWriter.h */, + E296AD2814D5DDBA006F96D2 /* SBJsonStreamWriter.m */, + E296AD2914D5DDBA006F96D2 /* SBJsonStreamWriterAccumulator.h */, + E296AD2A14D5DDBA006F96D2 /* SBJsonStreamWriterAccumulator.m */, + E296AD2B14D5DDBA006F96D2 /* SBJsonStreamWriterState.h */, + E296AD2C14D5DDBA006F96D2 /* SBJsonStreamWriterState.m */, + E296AD2D14D5DDBA006F96D2 /* SBJsonTokeniser.h */, + E296AD2E14D5DDBA006F96D2 /* SBJsonTokeniser.m */, + E296AD2F14D5DDBA006F96D2 /* SBJsonUTF8Stream.h */, + E296AD3014D5DDBA006F96D2 /* SBJsonUTF8Stream.m */, + E296AD3114D5DDBA006F96D2 /* SBJsonWriter.h */, + E296AD3214D5DDBA006F96D2 /* SBJsonWriter.m */, + ); + name = JSON; + sourceTree = ""; + }; + E299157F14D4E63000C5B7F3 = { + isa = PBXGroup; + children = ( + E296AD1914D5DD9A006F96D2 /* JSON */, + E299159414D4E63100C5B7F3 /* CardsAgainstHumanity */, + E299158D14D4E63100C5B7F3 /* Frameworks */, + E299158B14D4E63100C5B7F3 /* Products */, + ); + sourceTree = ""; + }; + E299158B14D4E63100C5B7F3 /* Products */ = { + isa = PBXGroup; + children = ( + E299158A14D4E63100C5B7F3 /* CardsAgainstHumanity.app */, + ); + name = Products; + sourceTree = ""; + }; + E299158D14D4E63100C5B7F3 /* Frameworks */ = { + isa = PBXGroup; + children = ( + E299158E14D4E63100C5B7F3 /* UIKit.framework */, + E299159014D4E63100C5B7F3 /* Foundation.framework */, + E299159214D4E63100C5B7F3 /* CoreGraphics.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + E299159414D4E63100C5B7F3 /* CardsAgainstHumanity */ = { + isa = PBXGroup; + children = ( + E29915AD14D4E69000C5B7F3 /* Room.h */, + E29915AE14D4E69000C5B7F3 /* Room.m */, + E29915A514D4E66D00C5B7F3 /* RoomViewController.h */, + E29915A614D4E66D00C5B7F3 /* RoomViewController.m */, + E29915A714D4E66D00C5B7F3 /* RoomViewController.xib */, + E299159D14D4E63100C5B7F3 /* AppDelegate.h */, + E299159E14D4E63100C5B7F3 /* AppDelegate.m */, + E299159514D4E63100C5B7F3 /* Supporting Files */, + E29915B014D4EA1800C5B7F3 /* WhiteCardsViewController.h */, + E29915B114D4EA1800C5B7F3 /* WhiteCardsViewController.m */, + E29915B214D4EA1800C5B7F3 /* WhiteCardsViewController.xib */, + E296ACA514D526C1006F96D2 /* BlackCardsViewController.h */, + E296ACA614D526C1006F96D2 /* BlackCardsViewController.m */, + E296ACA714D526C1006F96D2 /* BlackCardsViewController.xib */, + E296ACAF14D5B750006F96D2 /* CardsViewController.h */, + E296ACB014D5B750006F96D2 /* CardsViewController.m */, + E296ACB114D5B750006F96D2 /* CardsViewController.xib */, + E296AD1114D5D040006F96D2 /* LoginViewController.h */, + E296AD1214D5D041006F96D2 /* LoginViewController.m */, + E296AD1314D5D042006F96D2 /* LoginViewController.xib */, + ); + path = CardsAgainstHumanity; + sourceTree = ""; + }; + E299159514D4E63100C5B7F3 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + E299159614D4E63100C5B7F3 /* CardsAgainstHumanity-Info.plist */, + E299159714D4E63100C5B7F3 /* InfoPlist.strings */, + E299159A14D4E63100C5B7F3 /* main.m */, + E299159C14D4E63100C5B7F3 /* CardsAgainstHumanity-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + E299158914D4E63100C5B7F3 /* CardsAgainstHumanity */ = { + isa = PBXNativeTarget; + buildConfigurationList = E29915A214D4E63100C5B7F3 /* Build configuration list for PBXNativeTarget "CardsAgainstHumanity" */; + buildPhases = ( + E299158614D4E63100C5B7F3 /* Sources */, + E299158714D4E63100C5B7F3 /* Frameworks */, + E299158814D4E63100C5B7F3 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CardsAgainstHumanity; + productName = CardsAgainstHumanity; + productReference = E299158A14D4E63100C5B7F3 /* CardsAgainstHumanity.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + E299158114D4E63000C5B7F3 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0420; + }; + buildConfigurationList = E299158414D4E63000C5B7F3 /* Build configuration list for PBXProject "CardsAgainstHumanity" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = E299157F14D4E63000C5B7F3; + productRefGroup = E299158B14D4E63100C5B7F3 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + E299158914D4E63100C5B7F3 /* CardsAgainstHumanity */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + E299158814D4E63100C5B7F3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E299159914D4E63100C5B7F3 /* InfoPlist.strings in Resources */, + E29915A914D4E66D00C5B7F3 /* RoomViewController.xib in Resources */, + E29915B414D4EA1800C5B7F3 /* WhiteCardsViewController.xib in Resources */, + E296ACA914D526C1006F96D2 /* BlackCardsViewController.xib in Resources */, + E296ACB314D5B750006F96D2 /* CardsViewController.xib in Resources */, + E296AD1514D5D042006F96D2 /* LoginViewController.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + E299158614D4E63100C5B7F3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E299159B14D4E63100C5B7F3 /* main.m in Sources */, + E299159F14D4E63100C5B7F3 /* AppDelegate.m in Sources */, + E29915A814D4E66D00C5B7F3 /* RoomViewController.m in Sources */, + E29915AF14D4E69000C5B7F3 /* Room.m in Sources */, + E29915B314D4EA1800C5B7F3 /* WhiteCardsViewController.m in Sources */, + E296ACA814D526C1006F96D2 /* BlackCardsViewController.m in Sources */, + E296ACB214D5B750006F96D2 /* CardsViewController.m in Sources */, + E296AD1414D5D042006F96D2 /* LoginViewController.m in Sources */, + E296AD3314D5DDBA006F96D2 /* NSObject+SBJson.m in Sources */, + E296AD3414D5DDBA006F96D2 /* SBJsonParser.m in Sources */, + E296AD3514D5DDBA006F96D2 /* SBJsonStreamParser.m in Sources */, + E296AD3614D5DDBA006F96D2 /* SBJsonStreamParserAccumulator.m in Sources */, + E296AD3714D5DDBA006F96D2 /* SBJsonStreamParserAdapter.m in Sources */, + E296AD3814D5DDBA006F96D2 /* SBJsonStreamParserState.m in Sources */, + E296AD3914D5DDBA006F96D2 /* SBJsonStreamWriter.m in Sources */, + E296AD3A14D5DDBA006F96D2 /* SBJsonStreamWriterAccumulator.m in Sources */, + E296AD3B14D5DDBA006F96D2 /* SBJsonStreamWriterState.m in Sources */, + E296AD3C14D5DDBA006F96D2 /* SBJsonTokeniser.m in Sources */, + E296AD3D14D5DDBA006F96D2 /* SBJsonUTF8Stream.m in Sources */, + E296AD3E14D5DDBA006F96D2 /* SBJsonWriter.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + E299159714D4E63100C5B7F3 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + E299159814D4E63100C5B7F3 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + E29915A014D4E63100C5B7F3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + CLANG_ENABLE_OBJC_ARC = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; + SDKROOT = iphoneos; + }; + name = Debug; + }; + E29915A114D4E63100C5B7F3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + CLANG_ENABLE_OBJC_ARC = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; + OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + E29915A314D4E63100C5B7F3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "CardsAgainstHumanity/CardsAgainstHumanity-Prefix.pch"; + INFOPLIST_FILE = "CardsAgainstHumanity/CardsAgainstHumanity-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + E29915A414D4E63100C5B7F3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "CardsAgainstHumanity/CardsAgainstHumanity-Prefix.pch"; + INFOPLIST_FILE = "CardsAgainstHumanity/CardsAgainstHumanity-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + E299158414D4E63000C5B7F3 /* Build configuration list for PBXProject "CardsAgainstHumanity" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E29915A014D4E63100C5B7F3 /* Debug */, + E29915A114D4E63100C5B7F3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E29915A214D4E63100C5B7F3 /* Build configuration list for PBXNativeTarget "CardsAgainstHumanity" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E29915A314D4E63100C5B7F3 /* Debug */, + E29915A414D4E63100C5B7F3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = E299158114D4E63000C5B7F3 /* Project object */; +} diff --git a/CardsAgainstHumanity.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/CardsAgainstHumanity.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..a52b984 --- /dev/null +++ b/CardsAgainstHumanity.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/CardsAgainstHumanity.xcodeproj/project.xcworkspace/xcuserdata/vivek.xcuserdatad/UserInterfaceState.xcuserstate b/CardsAgainstHumanity.xcodeproj/project.xcworkspace/xcuserdata/vivek.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..e2675bcb56fc52ff0f733c04a3f9a05fb6c3d785 GIT binary patch literal 27599 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 literal 0 HcmV?d00001 diff --git a/CardsAgainstHumanity.xcodeproj/xcuserdata/vivek.xcuserdatad/xcschemes/CardsAgainstHumanity.xcscheme b/CardsAgainstHumanity.xcodeproj/xcuserdata/vivek.xcuserdatad/xcschemes/CardsAgainstHumanity.xcscheme new file mode 100644 index 0000000..a35055b --- /dev/null +++ b/CardsAgainstHumanity.xcodeproj/xcuserdata/vivek.xcuserdatad/xcschemes/CardsAgainstHumanity.xcscheme @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CardsAgainstHumanity.xcodeproj/xcuserdata/vivek.xcuserdatad/xcschemes/xcschememanagement.plist b/CardsAgainstHumanity.xcodeproj/xcuserdata/vivek.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..8b3b610 --- /dev/null +++ b/CardsAgainstHumanity.xcodeproj/xcuserdata/vivek.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + CardsAgainstHumanity.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + E299158914D4E63100C5B7F3 + + primary + + + + + diff --git a/CardsAgainstHumanity/AppDelegate.h b/CardsAgainstHumanity/AppDelegate.h new file mode 100644 index 0000000..16c768b --- /dev/null +++ b/CardsAgainstHumanity/AppDelegate.h @@ -0,0 +1,20 @@ +// +// AppDelegate.h +// CardsAgainstHumanity +// +// Created by Vivek Bhagwat on 1/28/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import + +@class RoomViewController; + +@interface AppDelegate : UIResponder +{ + UINavigationController *navigationController; +} +@property (strong, nonatomic) UINavigationController *navigationController; +@property (strong, nonatomic) UIWindow *window; + +@end diff --git a/CardsAgainstHumanity/AppDelegate.m b/CardsAgainstHumanity/AppDelegate.m new file mode 100644 index 0000000..22f1e60 --- /dev/null +++ b/CardsAgainstHumanity/AppDelegate.m @@ -0,0 +1,86 @@ +// +// AppDelegate.m +// CardsAgainstHumanity +// +// Created by Vivek Bhagwat on 1/28/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import "AppDelegate.h" +#import "RoomViewController.h" +#import "LoginViewController.h" + +@implementation AppDelegate + +@synthesize navigationController;// = _navigationController; +@synthesize window = _window; + +- (BOOL)application:(UIApplication *)application +didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + // Override point for customization after application launch. +// UIViewController *rootController = [[RoomViewController alloc] initWithNibName:@"RoomViewController" bundle:nil]; + UIViewController *rootController = [[LoginViewController alloc] initWithNibName:@"LoginViewController" bundle:nil]; + + navigationController = [[UINavigationController alloc] + initWithRootViewController:rootController]; + + self.window = [[UIWindow alloc] + initWithFrame:[[UIScreen mainScreen] bounds]]; + [self.window addSubview:navigationController.view]; + [self.window makeKeyAndVisible]; + return YES; + + /* + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + // Override point for customization after application launch. + self.window.backgroundColor = [UIColor whiteColor]; + [self.window makeKeyAndVisible]; + return YES; + + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + [self.window makeKeyAndVisible]; + return YES; + */ +} + +- (void)applicationWillResignActive:(UIApplication *)application +{ + /* + Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. + */ +} + +- (void)applicationDidEnterBackground:(UIApplication *)application +{ + /* + Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + */ +} + +- (void)applicationWillEnterForeground:(UIApplication *)application +{ + /* + Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. + */ +} + +- (void)applicationDidBecomeActive:(UIApplication *)application +{ + /* + Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + */ +} + +- (void)applicationWillTerminate:(UIApplication *)application +{ + /* + Called when the application is about to terminate. + Save data if appropriate. + See also applicationDidEnterBackground:. + */ +} + +@end diff --git a/CardsAgainstHumanity/BlackCardsViewController.h b/CardsAgainstHumanity/BlackCardsViewController.h new file mode 100644 index 0000000..9dc419c --- /dev/null +++ b/CardsAgainstHumanity/BlackCardsViewController.h @@ -0,0 +1,18 @@ +// +// BlackCardsViewController.h +// CardsAgainstHumanity +// +// Created by Vivek Bhagwat on 1/29/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import + +@interface BlackCardsViewController : UIViewController +{ + NSString *blackCard; + NSArray *whiteCards; +} +@property (strong, nonatomic) NSArray *whiteCards; +@property (strong, nonatomic) NSString *blackCard; +@end diff --git a/CardsAgainstHumanity/BlackCardsViewController.m b/CardsAgainstHumanity/BlackCardsViewController.m new file mode 100644 index 0000000..116d194 --- /dev/null +++ b/CardsAgainstHumanity/BlackCardsViewController.m @@ -0,0 +1,65 @@ +// +// BlackCardsViewController.m +// CardsAgainstHumanity +// +// Created by Vivek Bhagwat on 1/29/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import "BlackCardsViewController.h" + +@implementation BlackCardsViewController +@synthesize blackCard; +@synthesize whiteCards; + + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + self.title = NSLocalizedString(@"Black", @"Black"); + // Custom initialization + } + return self; +} + +- (void)didReceiveMemoryWarning +{ + // Releases the view if it doesn't have a superview. + [super didReceiveMemoryWarning]; + + // Release any cached data, images, etc that aren't in use. +} + +#pragma mark - View lifecycle + +- (void)viewDidLoad +{ + [super viewDidLoad]; + // Do any additional setup after loading the view from its nib. +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + + //get card Data + whiteCards = [[NSArray alloc] initWithObjects:@"Masturbation", @"Golden Showers", nil]; + blackCard = @"My favorite thing in the morning is a ___"; + + // [self. reloadData]; +} + +- (void)viewDidUnload +{ + [super viewDidUnload]; + // Release any retained subviews of the main view. + // e.g. self.myOutlet = nil; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + // Return YES for supported orientations + return (interfaceOrientation == UIInterfaceOrientationPortrait); +} + +@end diff --git a/CardsAgainstHumanity/BlackCardsViewController.xib b/CardsAgainstHumanity/BlackCardsViewController.xib new file mode 100644 index 0000000..ff8178e --- /dev/null +++ b/CardsAgainstHumanity/BlackCardsViewController.xib @@ -0,0 +1,161 @@ + + + + 1280 + 10K540 + 1938 + 1038.36 + 461.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 933 + + + IBProxyObject + IBUIView + IBUILabel + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + + 274 + + + + 292 + {{139, 219}, {43, 21}} + + + + NO + YES + 7 + NO + IBCocoaTouchFramework + Black + + 1 + MCAwIDAAA + + + 1 + 10 + + 1 + 17 + + + Helvetica + 17 + 16 + + + + {{0, 20}, {320, 460}} + + + + + 3 + MQA + + 2 + + + + IBCocoaTouchFramework + + + + + + + view + + + + 3 + + + + + + 0 + + + + + + 1 + + + + + + + + -1 + + + File's Owner + + + -2 + + + + + 8 + + + + + + + BlackCardsViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + 8 + + + + + BlackCardsViewController + UIViewController + + IBProjectSource + ./Classes/BlackCardsViewController.h + + + + + 0 + IBCocoaTouchFramework + YES + 3 + 933 + + diff --git a/CardsAgainstHumanity/CardsAgainstHumanity-Info.plist b/CardsAgainstHumanity/CardsAgainstHumanity-Info.plist new file mode 100644 index 0000000..c5f20b3 --- /dev/null +++ b/CardsAgainstHumanity/CardsAgainstHumanity-Info.plist @@ -0,0 +1,40 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFiles + + CFBundleIdentifier + vivekbhagwat.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/CardsAgainstHumanity/CardsAgainstHumanity-Prefix.pch b/CardsAgainstHumanity/CardsAgainstHumanity-Prefix.pch new file mode 100644 index 0000000..d735808 --- /dev/null +++ b/CardsAgainstHumanity/CardsAgainstHumanity-Prefix.pch @@ -0,0 +1,14 @@ +// +// Prefix header for all source files of the 'CardsAgainstHumanity' target in the 'CardsAgainstHumanity' project +// + +#import + +#ifndef __IPHONE_3_0 +#warning "This project uses features only available in iOS SDK 3.0 and later." +#endif + +#ifdef __OBJC__ + #import + #import +#endif diff --git a/CardsAgainstHumanity/CardsViewController.h b/CardsAgainstHumanity/CardsViewController.h new file mode 100644 index 0000000..ef47cfe --- /dev/null +++ b/CardsAgainstHumanity/CardsViewController.h @@ -0,0 +1,22 @@ +// +// CardsViewController.h +// CardsAgainstHumanity +// +// Created by Vivek Bhagwat on 1/29/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +@class WhiteCardsViewController; +@class BlackCardsViewController; + +@interface CardsViewController : UITabBarController { + UITabBar *cardBar; + WhiteCardsViewController *white; + BlackCardsViewController *black; + +} +@property (strong, nonatomic) IBOutlet UITabBar *cardBar; +@property (strong, nonatomic) IBOutlet WhiteCardsViewController *white; +@property (strong, nonatomic) IBOutlet BlackCardsViewController *black; + +@end diff --git a/CardsAgainstHumanity/CardsViewController.m b/CardsAgainstHumanity/CardsViewController.m new file mode 100644 index 0000000..8977924 --- /dev/null +++ b/CardsAgainstHumanity/CardsViewController.m @@ -0,0 +1,73 @@ +// +// CardsViewController.m +// CardsAgainstHumanity +// +// Created by Vivek Bhagwat on 1/29/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import "CardsViewController.h" +#import "WhiteCardsViewController.h" +#import "BlackCardsViewController.h" + +@implementation CardsViewController +@synthesize cardBar; +@synthesize white; +@synthesize black; + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + NSLog(@"init with nib name cards view controller"); + // Custom initialization + self.white = [[WhiteCardsViewController alloc] initWithNibName:@"WhiteCardsViewController" bundle:nil]; + self.black = [[BlackCardsViewController alloc] initWithNibName:@"BlackCardsViewController" bundle:nil]; + self.cardBar = [[UITabBar alloc] init]; + self.viewControllers = [NSArray arrayWithObjects:self.white,self.black, nil]; +// self.cardsController = [[UITabBarController alloc] init]; +// self.cardsController.viewControllers = [NSArray arrayWithObjects:white, black, nil]; + } + return self; +} + +- (void)didReceiveMemoryWarning +{ + // Releases the view if it doesn't have a superview. + [super didReceiveMemoryWarning]; + + // Release any cached data, images, etc that aren't in use. +} + +#pragma mark - View lifecycle + +- (void)viewDidLoad +{ + [super viewDidLoad]; + // Do any additional setup after loading the view from its nib. +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + + //get card Data + NSLog(@"IN THIS METHOD HERE"); + + + // [self. reloadData]; +} + +- (void)viewDidUnload +{ + [super viewDidUnload]; + // Release any retained subviews of the main view. + // e.g. self.myOutlet = nil; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + // Return YES for supported orientations + return (interfaceOrientation == UIInterfaceOrientationPortrait); +} + +@end diff --git a/CardsAgainstHumanity/CardsViewController.xib b/CardsAgainstHumanity/CardsViewController.xib new file mode 100644 index 0000000..78400ce --- /dev/null +++ b/CardsAgainstHumanity/CardsViewController.xib @@ -0,0 +1,237 @@ + + + + 1280 + 10K540 + 1938 + 1038.36 + 461.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 933 + + + IBUITabBar + IBUITabBarController + IBUIViewController + IBUIView + IBUITabBarItem + IBProxyObject + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + + 274 + {{0, 20}, {320, 460}} + + + + 3 + MQA + + 2 + + + + IBCocoaTouchFramework + + + + + + 1 + 1 + + YES + IBCocoaTouchFramework + NO + + + White + IBCocoaTouchFramework + + + + 1 + 1 + + IBCocoaTouchFramework + NO + + + + + + Black + IBCocoaTouchFramework + + + + 1 + 1 + + IBCocoaTouchFramework + NO + + + + + 266 + {{0, 431}, {320, 49}} + + + + 3 + MCAwAA + + IBCocoaTouchFramework + + + + + + + + view + + + + 3 + + + + black + + + + 14 + + + + white + + + + 15 + + + + cardBar + + + + 18 + + + + + + 0 + + + + + + 1 + + + + + -1 + + + File's Owner + + + -2 + + + + + 4 + + + + + + + + + + 5 + + + + + 6 + + + + + + + + 7 + + + + + + + + 8 + + + + + 9 + + + + + + + CardsViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + CardsViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + WhiteCardsViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + BlackCardsViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + 18 + + + 0 + IBCocoaTouchFramework + YES + 3 + 933 + + diff --git a/CardsAgainstHumanity/LoginViewController.h b/CardsAgainstHumanity/LoginViewController.h new file mode 100644 index 0000000..b12455c --- /dev/null +++ b/CardsAgainstHumanity/LoginViewController.h @@ -0,0 +1,23 @@ +// +// LoginViewController.h +// CardsAgainstHumanity +// +// Created by Vivek Bhagwat on 1/29/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import + +@interface LoginViewController : UIViewController +{ + UITextField *nameField; + UIButton *login; + UIGestureRecognizer *tap; +} +@property (strong, nonatomic) IBOutlet UITextField *nameField; +@property (strong, nonatomic) IBOutlet UIButton *login; +@property (strong, nonatomic) UIGestureRecognizer *tap; + +- (IBAction) login: (id) sender; + +@end diff --git a/CardsAgainstHumanity/LoginViewController.m b/CardsAgainstHumanity/LoginViewController.m new file mode 100644 index 0000000..d08d801 --- /dev/null +++ b/CardsAgainstHumanity/LoginViewController.m @@ -0,0 +1,129 @@ +// +// LoginViewController.m +// CardsAgainstHumanity +// +// Created by Vivek Bhagwat on 1/29/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import "LoginViewController.h" +#import "RoomViewController.h" +#import "SBJson.h" + +@interface LoginViewController () +@end + +@implementation LoginViewController +@synthesize nameField; +@synthesize login; +@synthesize tap; + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + self.nameField = [[UITextField alloc] init]; + self.login = [[UIButton alloc] init]; + // Custom initialization + } + return self; +} + +- (void)didReceiveMemoryWarning +{ + // Releases the view if it doesn't have a superview. + [super didReceiveMemoryWarning]; + + // Release any cached data, images, etc that aren't in use. +} + +#pragma mark - View lifecycle + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.tap = [[UITapGestureRecognizer alloc] initWithTarget:self + action:@selector(dismissKeyboard)]; + + [self.view addGestureRecognizer:self.tap]; + // Do any additional setup after loading the view from its nib. +} + +- (void)viewDidUnload +{ + [super viewDidUnload]; + // Release any retained subviews of the main view. + // e.g. self.myOutlet = nil; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + // Return YES for supported orientations + return (interfaceOrientation == UIInterfaceOrientationPortrait); +} + +- (IBAction) login: (id) sender +{ + login.enabled = NO; + + NSString *name = [nameField text]; +// NSMutableDictionary *user = [[NSMutableDictionary alloc] initWithObjects:[[NSArray alloc] initWithObjects:name, nil] forKeys:[[NSArray alloc] initWithObjects:@"name", nil]]; + NSMutableDictionary *user = [[NSMutableDictionary alloc] init]; + [user setObject:[[NSMutableDictionary alloc] init] forKey:@"user"]; + [[user objectForKey:@"user"] setObject:name forKey:@"name"]; + + + NSString *body = [user JSONRepresentation]; + + NSMutableURLRequest *req = [[NSMutableURLRequest alloc] + initWithURL:[[NSURL alloc] initWithString:@"http://radiant-moon-9602.herokuapp.com/users/new"]]; + + [req setHTTPMethod:@"PUT"]; + [req setValue:@"application/json" forHTTPHeaderField:@"content-type"]; + [req setHTTPBody:[NSData dataWithBytes:[body UTF8String] length:[body length]]]; + + NSURLConnection *con = [[NSURLConnection alloc] initWithRequest:req delegate:self]; + [con start]; +} + +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response +{ + NSLog(@"connection did receive response"); +} + + +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data +{ + NSLog(@"connection did receive data"); + NSLog([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); + + NSDictionary *rooms = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] JSONRepresentation]; + NSLog([rooms description]); +// [self.navigationController pushViewController:[[RoomViewController alloc] initWithNibName:@"RoomViewController" bundle:nil] animated:YES]; +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)connection +{ + NSLog(@"connection did finish loading"); +} + +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Connection Error" + message:@"Could not join room." + delegate:nil + cancelButtonTitle:@"OK" + otherButtonTitles:nil]; + // Display the alert to the user + [alert show]; + [self.navigationController popViewControllerAnimated:NO]; + alert = nil; +} + +-(void)dismissKeyboard { + [nameField resignFirstResponder]; + [self.view removeGestureRecognizer:self.tap]; +} + +@end diff --git a/CardsAgainstHumanity/LoginViewController.xib b/CardsAgainstHumanity/LoginViewController.xib new file mode 100644 index 0000000..f84414f --- /dev/null +++ b/CardsAgainstHumanity/LoginViewController.xib @@ -0,0 +1,295 @@ + + + + 1280 + 10K540 + 1938 + 1038.36 + 461.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 933 + + + IBUILabel + IBUIButton + IBUIView + IBUITextField + IBProxyObject + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + + 274 + + + + 292 + {{86, 172}, {149, 31}} + + + + NO + YES + IBCocoaTouchFramework + 0 + + 3 + + 3 + MAA + + 2 + + + YES + 17 + + IBCocoaTouchFramework + + + 1 + 14 + + + Helvetica + 14 + 16 + + + + + 292 + {{126, 211}, {72, 37}} + + + + NO + IBCocoaTouchFramework + 0 + 0 + 1 + Login + + 3 + MQA + + + 1 + MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA + + + 3 + MC41AA + + + 2 + 15 + + + Helvetica-Bold + 15 + 16 + + + + + 292 + {{137, 143}, {46, 21}} + + + + NO + YES + 7 + NO + IBCocoaTouchFramework + Name + + 1 + MCAwIDAAA + + + 1 + 10 + + 1 + 17 + + + Helvetica + 17 + 16 + + + + {{0, 20}, {320, 460}} + + + + + 3 + MQA + + + + IBCocoaTouchFramework + + + + + + + view + + + + 3 + + + + nameField + + + + 9 + + + + login + + + + 10 + + + + login: + + + 7 + + 11 + + + + + + 0 + + + + + + 1 + + + + + + + + + + -1 + + + File's Owner + + + -2 + + + + + 4 + + + + + 5 + + + + + 8 + + + + + + + LoginViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + 11 + + + + + LoginViewController + UIViewController + + login: + id + + + login: + + login: + id + + + + UIButton + UITextField + + + + login + UIButton + + + nameField + UITextField + + + + IBProjectSource + ./Classes/LoginViewController.h + + + + + 0 + IBCocoaTouchFramework + YES + 3 + 933 + + diff --git a/CardsAgainstHumanity/Room.h b/CardsAgainstHumanity/Room.h new file mode 100644 index 0000000..44eeb4f --- /dev/null +++ b/CardsAgainstHumanity/Room.h @@ -0,0 +1,13 @@ +// +// Room.h +// CardsAgainstHumanity +// +// Created by Vivek Bhagwat on 1/28/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import + +@interface Room : NSObject + +@end diff --git a/CardsAgainstHumanity/Room.m b/CardsAgainstHumanity/Room.m new file mode 100644 index 0000000..a6db1ac --- /dev/null +++ b/CardsAgainstHumanity/Room.m @@ -0,0 +1,13 @@ +// +// Room.m +// CardsAgainstHumanity +// +// Created by Vivek Bhagwat on 1/28/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import "Room.h" + +@implementation Room + +@end diff --git a/CardsAgainstHumanity/RoomViewController.h b/CardsAgainstHumanity/RoomViewController.h new file mode 100644 index 0000000..8499ecf --- /dev/null +++ b/CardsAgainstHumanity/RoomViewController.h @@ -0,0 +1,20 @@ +// +// RoomViewController.h +// CardsAgainstHumanity +// +// Created by Vivek Bhagwat on 1/28/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import + +@class CardsViewController; + +@interface RoomViewController : UITableViewController +{ + NSArray *roomList; + CardsViewController *cardsController; +} +@property (nonatomic, strong) NSArray *roomList; +@property (nonatomic, strong) IBOutlet CardsViewController *cardsController; +@end diff --git a/CardsAgainstHumanity/RoomViewController.m b/CardsAgainstHumanity/RoomViewController.m new file mode 100644 index 0000000..f3cf78c --- /dev/null +++ b/CardsAgainstHumanity/RoomViewController.m @@ -0,0 +1,239 @@ +// +// RoomViewController.m +// CardsAgainstHumanity +// +// Created by Vivek Bhagwat on 1/28/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import "RoomViewController.h" +#import "CardsViewController.h" + +@interface RoomViewController () +@end + +@implementation RoomViewController + +@synthesize roomList; +@synthesize cardsController; + +- (id)initWithStyle:(UITableViewStyle)style +{ + self = [super initWithStyle:style]; + if (self) { + // Custom initialization + } + return self; +} + +- (void)didReceiveMemoryWarning +{ + // Releases the view if it doesn't have a superview. + [super didReceiveMemoryWarning]; + + // Release any cached data, images, etc that aren't in use. +} + +#pragma mark - View lifecycle + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + + //download room info, parse and sort into objects + + self.roomList = [[NSArray alloc] initWithObjects:@"Room 1", @"Room 2", nil]; + self.title = @"Rooms"; + + // Uncomment the following line to preserve selection between presentations. + // self.clearsSelectionOnViewWillAppear = NO; + + // Uncomment the following line to display an Edit button in the navigation bar for this view controller. + // self.navigationItem.rightBarButtonItem = self.editButtonItem; +} + +- (void)viewDidUnload +{ + [super viewDidUnload]; + self.roomList = nil; + // Release any retained subviews of the main view. + // e.g. self.myOutlet = nil; +} + +- (void)viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; +} + +- (void)viewDidAppear:(BOOL)animated +{ + [super viewDidAppear:animated]; +} + +- (void)viewWillDisappear:(BOOL)animated +{ + [super viewWillDisappear:animated]; +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear:animated]; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + // Return YES for supported orientations + return (interfaceOrientation == UIInterfaceOrientationPortrait); +} + +#pragma mark - Table view data source + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView +{ + //#warning Potentially incomplete method implementation. + // Return the number of sections. + return 1; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + //#warning Incomplete method implementation. + // Return the number of rows in the section. + return [self.roomList count]; +} + + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + + static NSString *CellIdentifier = @"Cell"; + + UITableViewCell *cell = [tableView + dequeueReusableCellWithIdentifier:CellIdentifier]; + if (cell == nil) { + cell = [[UITableViewCell alloc] + initWithStyle:UITableViewCellStyleDefault + reuseIdentifier:CellIdentifier]; + } + // Configure the cell. + cell.textLabel.text = + [self.roomList objectAtIndex: + [indexPath row]]; + return cell; +} + +/* + // Override to support conditional editing of the table view. + - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath + { + // Return NO if you do not want the specified item to be editable. + return YES; + } + */ + +/* + // Override to support editing the table view. + - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath + { + if (editingStyle == UITableViewCellEditingStyleDelete) { + // Delete the row from the data source + [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; + } + else if (editingStyle == UITableViewCellEditingStyleInsert) { + // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view + } + } + */ + +/* + // Override to support rearranging the table view. + - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath + { + } + */ + +/* + // Override to support conditional rearranging of the table view. + - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath + { + // Return NO if you do not want the item to be re-orderable. + return YES; + } + */ + +#pragma mark - Table view delegate + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + /* + <#DetailViewController#> *detailViewController = + [[<#DetailViewController#> alloc] + initWithNibName:@"<#Nib name#>" + bundle:nil]; + // ... + // Pass the selected object to the new view controller. + + [self.navigationController + pushViewController:detailViewController + animated:YES]; + [detailViewController release]; + */ + + NSMutableURLRequest *req = [[NSMutableURLRequest alloc] + initWithURL:[[NSURL alloc] initWithString:@"http://localhost/"]]; + + [req setHTTPMethod:@"GET"]; + + NSURLConnection *con = [[NSURLConnection alloc] initWithRequest:req delegate:self]; + [con start]; +// [self.navigationController pushViewController:self.cardsController animated:YES]; + + +// self.cardsController.title = [[roomList objectAtIndex:indexPath.row] description]; +} + +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response +{ + NSLog(@"connection did receive response"); + NSLog([response description]); +} + + +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data +{ + NSLog(@"connection did receive data"); + NSLog([data description]); + + NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + NSLog(str); + + if(YES || str == @"

It works!

") //room joined successfully + { + self.cardsController = [[CardsViewController alloc] initWithNibName:@"CardsViewController" bundle:nil]; + [self.navigationController pushViewController:self.cardsController animated:YES]; + } +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)connection +{ + NSLog(@"connection did finish loading"); + NSLog([connection description]); +} + +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Connection Error" + message:@"Could not join room." + delegate:nil + cancelButtonTitle:@"OK" + otherButtonTitles:nil]; + // Display the alert to the user + [alert show]; + [self.navigationController popViewControllerAnimated:NO]; + alert = nil; +} + + + + +@end diff --git a/CardsAgainstHumanity/RoomViewController.xib b/CardsAgainstHumanity/RoomViewController.xib new file mode 100644 index 0000000..d37de4f --- /dev/null +++ b/CardsAgainstHumanity/RoomViewController.xib @@ -0,0 +1,186 @@ + + + + 1280 + 10K540 + 1938 + 1038.36 + 461.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 933 + + + IBProxyObject + IBUIViewController + IBUITableView + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + + 274 + {{0, 20}, {320, 460}} + + + + + 3 + MQA + + NO + YES + NO + + IBCocoaTouchFramework + NO + 1 + 0 + YES + 44 + 22 + 22 + + + + + 1 + 1 + + IBCocoaTouchFramework + NO + + + + + + + view + + + + 5 + + + + cardsController + + + + 20 + + + + dataSource + + + + 6 + + + + delegate + + + + 7 + + + + + + 0 + + + + + + -1 + + + File's Owner + + + -2 + + + + + 4 + + + + + 19 + + + + + + + RoomViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + CardsViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + 20 + + + + + CardsViewController + UIViewController + + IBProjectSource + ./Classes/CardsViewController.h + + + + RoomViewController + UITableViewController + + cardsController + CardsViewController + + + cardsController + + cardsController + CardsViewController + + + + IBProjectSource + ./Classes/RoomViewController.h + + + + + 0 + IBCocoaTouchFramework + YES + 3 + 933 + + diff --git a/CardsAgainstHumanity/WhiteCardsViewController.h b/CardsAgainstHumanity/WhiteCardsViewController.h new file mode 100644 index 0000000..fb86abc --- /dev/null +++ b/CardsAgainstHumanity/WhiteCardsViewController.h @@ -0,0 +1,17 @@ +// +// WhiteCardsViewController.h +// CardsAgainstHumanity +// +// Created by Vivek Bhagwat on 1/28/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import +@class BlackCardsViewController; + +@interface WhiteCardsViewController : UIViewController { + NSArray *whiteCards; +} +@property (strong, nonatomic) NSArray *whiteCards; + +@end diff --git a/CardsAgainstHumanity/WhiteCardsViewController.m b/CardsAgainstHumanity/WhiteCardsViewController.m new file mode 100644 index 0000000..ede4ae6 --- /dev/null +++ b/CardsAgainstHumanity/WhiteCardsViewController.m @@ -0,0 +1,70 @@ +// +// WhiteCardsViewController.m +// CardsAgainstHumanity +// +// Created by Vivek Bhagwat on 1/28/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import "WhiteCardsViewController.h" +#import "BlackCardsViewController.h" + +@interface WhiteCardsViewController () +//@property (strong, nonatomic) UITabBarController *tabBarController; +@end + +@implementation WhiteCardsViewController +@synthesize whiteCards; +//@synthesize title; + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + NSLog(@"init With Nib Name"); + self.title = NSLocalizedString(@"White", @"White"); + // Custom initialization + } + return self; +} + +- (void)didReceiveMemoryWarning +{ + // Releases the view if it doesn't have a superview. + [super didReceiveMemoryWarning]; + + // Release any cached data, images, etc that aren't in use. +} + +#pragma mark - View lifecycle + +- (void)viewDidLoad +{ + [super viewDidLoad]; + // Do any additional setup after loading the view from its nib. +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + //get card Data + whiteCards = [[NSArray alloc] initWithObjects:@"Masturbation", @"Friction", @"Sex", @"Golden Showers", nil]; + + +// [self. reloadData]; +} + +- (void)viewDidUnload +{ + [super viewDidUnload]; + // Release any retained subviews of the main view. + // e.g. self.myOutlet = nil; + self.whiteCards = nil; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + // Return YES for supported orientations + return (interfaceOrientation == UIInterfaceOrientationPortrait); +} + +@end diff --git a/CardsAgainstHumanity/WhiteCardsViewController.xib b/CardsAgainstHumanity/WhiteCardsViewController.xib new file mode 100644 index 0000000..3439549 --- /dev/null +++ b/CardsAgainstHumanity/WhiteCardsViewController.xib @@ -0,0 +1,166 @@ + + + + 1280 + 10K540 + 1938 + 1038.36 + 461.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 933 + + + IBProxyObject + IBUIView + IBUIButton + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + + 274 + + + + 292 + {{124, 211}, {72, 37}} + + + + NO + IBCocoaTouchFramework + 0 + 0 + 1 + White + + 3 + MQA + + + 1 + MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA + + + 3 + MC41AA + + + 2 + 15 + + + Helvetica-Bold + 15 + 16 + + + + {{0, 20}, {320, 460}} + + + + + 3 + MQA + + 2 + + + + IBCocoaTouchFramework + + + + + + + view + + + + 3 + + + + + + 0 + + + + + + 1 + + + + + + + + -1 + + + File's Owner + + + -2 + + + + + 61 + + + + + + + WhiteCardsViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + 61 + + + + + WhiteCardsViewController + UIViewController + + IBProjectSource + ./Classes/WhiteCardsViewController.h + + + + + 0 + IBCocoaTouchFramework + YES + 3 + 933 + + diff --git a/CardsAgainstHumanity/en.lproj/InfoPlist.strings b/CardsAgainstHumanity/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/CardsAgainstHumanity/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/CardsAgainstHumanity/main.m b/CardsAgainstHumanity/main.m new file mode 100644 index 0000000..42b6d27 --- /dev/null +++ b/CardsAgainstHumanity/main.m @@ -0,0 +1,18 @@ +// +// main.m +// CardsAgainstHumanity +// +// Created by Vivek Bhagwat on 1/28/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import + +#import "AppDelegate.h" + +int main(int argc, char *argv[]) +{ + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/NSObject+SBJson.h b/NSObject+SBJson.h new file mode 100755 index 0000000..c9eeaa5 --- /dev/null +++ b/NSObject+SBJson.h @@ -0,0 +1,79 @@ +/* + Copyright (C) 2009 Stig Brautaset. All rights reserved. + + 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 author nor the names of its contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + + 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. + */ + +#import + +#pragma mark JSON Writing + +/// Adds JSON generation to NSObject +@interface NSObject (NSObject_SBJsonWriting) + +/** + @brief Encodes the receiver into a JSON string + + Although defined as a category on NSObject it is only defined for NSArray and NSDictionary. + + @return the receiver encoded in JSON, or nil on error. + + @see @ref objc2json + */ +- (NSString *)JSONRepresentation; + +@end + + +#pragma mark JSON Parsing + +/// Adds JSON parsing methods to NSString +@interface NSString (NSString_SBJsonParsing) + +/** + @brief Decodes the receiver's JSON text + + @return the NSDictionary or NSArray represented by the receiver, or nil on error. + + @see @ref json2objc + */ +- (id)JSONValue; + +@end + +/// Adds JSON parsing methods to NSData +@interface NSData (NSData_SBJsonParsing) + +/** + @brief Decodes the receiver's JSON data + + @return the NSDictionary or NSArray represented by the receiver, or nil on error. + + @see @ref json2objc + */ +- (id)JSONValue; + +@end diff --git a/NSObject+SBJson.m b/NSObject+SBJson.m new file mode 100755 index 0000000..62b0987 --- /dev/null +++ b/NSObject+SBJson.m @@ -0,0 +1,72 @@ +/* + Copyright (C) 2009 Stig Brautaset. All rights reserved. + + 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 author nor the names of its contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + + 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. + */ + +#import "NSObject+SBJson.h" +#import "SBJsonWriter.h" +#import "SBJsonParser.h" + +@implementation NSObject (NSObject_SBJsonWriting) + +- (NSString *)JSONRepresentation { + SBJsonWriter *writer = [[SBJsonWriter alloc] init]; + NSString *json = [writer stringWithObject:self]; + if (!json) + NSLog(@"-JSONRepresentation failed. Error is: %@", writer.error); + return json; +} + +@end + + + +@implementation NSString (NSString_SBJsonParsing) + +- (id)JSONValue { + SBJsonParser *parser = [[SBJsonParser alloc] init]; + id repr = [parser objectWithString:self]; + if (!repr) + NSLog(@"-JSONValue failed. Error is: %@", parser.error); + return repr; +} + +@end + + + +@implementation NSData (NSData_SBJsonParsing) + +- (id)JSONValue { + SBJsonParser *parser = [[SBJsonParser alloc] init]; + id repr = [parser objectWithData:self]; + if (!repr) + NSLog(@"-JSONValue failed. Error is: %@", parser.error); + return repr; +} + +@end diff --git a/SBJson.h b/SBJson.h new file mode 100755 index 0000000..b25da4a --- /dev/null +++ b/SBJson.h @@ -0,0 +1,84 @@ +/* + Copyright (C) 2009-2011 Stig Brautaset. All rights reserved. + + 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 author nor the names of its contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + + 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. + */ + +/** + @page json2objc JSON to Objective-C + + JSON is mapped to Objective-C types in the following way: + + @li null -> NSNull + @li string -> NSString + @li array -> NSMutableArray + @li object -> NSMutableDictionary + @li true -> NSNumber's -numberWithBool:YES + @li false -> NSNumber's -numberWithBool:NO + @li integer up to 19 digits -> NSNumber's -numberWithLongLong: + @li all other numbers -> NSDecimalNumber + + Since Objective-C doesn't have a dedicated class for boolean values, + these turns into NSNumber instances. However, since these are + initialised with the -initWithBool: method they round-trip back to JSON + properly. In other words, they won't silently suddenly become 0 or 1; + they'll be represented as 'true' and 'false' again. + + As an optimisation integers up to 19 digits in length (the max length + for signed long long integers) turn into NSNumber instances, while + complex ones turn into NSDecimalNumber instances. We can thus avoid any + loss of precision as JSON allows ridiculously large numbers. + + @page objc2json Objective-C to JSON + + Objective-C types are mapped to JSON types in the following way: + + @li NSNull -> null + @li NSString -> string + @li NSArray -> array + @li NSDictionary -> object + @li NSNumber's -initWithBool:YES -> true + @li NSNumber's -initWithBool:NO -> false + @li NSNumber -> number + + @note In JSON the keys of an object must be strings. NSDictionary + keys need not be, but attempting to convert an NSDictionary with + non-string keys into JSON will throw an exception. + + NSNumber instances created with the -numberWithBool: method are + converted into the JSON boolean "true" and "false" values, and vice + versa. Any other NSNumber instances are converted to a JSON number the + way you would expect. + + */ + +#import "SBJsonParser.h" +#import "SBJsonWriter.h" +#import "SBJsonStreamParser.h" +#import "SBJsonStreamParserAdapter.h" +#import "SBJsonStreamWriter.h" +#import "NSObject+SBJson.h" + diff --git a/SBJsonParser.h b/SBJsonParser.h new file mode 100755 index 0000000..751122f --- /dev/null +++ b/SBJsonParser.h @@ -0,0 +1,101 @@ +/* + Copyright (C) 2009 Stig Brautaset. All rights reserved. + + 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 author nor the names of its contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + + 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. + */ + +#import + +/** + @brief Parse JSON Strings and NSData objects + + This uses SBJsonStreamParser internally. + + @see @ref objc2json + + */ + +@interface SBJsonParser : NSObject + +/** + @brief The maximum recursing depth. + + Defaults to 32. If the input is nested deeper than this the input will be deemed to be + malicious and the parser returns nil, signalling an error. ("Nested too deep".) You can + turn off this security feature by setting the maxDepth value to 0. + */ +@property NSUInteger maxDepth; + +/** + @brief Description of parse error + + This method returns the trace of the last method that failed. + You need to check the return value of the call you're making to figure out + if the call actually failed, before you know call this method. + + @return A string describing the error encountered, or nil if no error occured. + + */ +@property(copy) NSString *error; + +/** + @brief Return the object represented by the given NSData object. + + The data *must* be UTF8 encoded. + + @param data An NSData containing UTF8 encoded data to parse. + @return The NSArray or NSDictionary represented by the object, or nil if an error occured. + + */ +- (id)objectWithData:(NSData*)data; + +/** + @brief Return the object represented by the given string + + This method converts its input to an NSData object containing UTF8 and calls -objectWithData: with it. + + @return The NSArray or NSDictionary represented by the object, or nil if an error occured. + */ +- (id)objectWithString:(NSString *)repr; + +/** + @brief Return the object represented by the given string + + This method calls objectWithString: internally. If an error occurs, and if @p error + is not nil, it creates an NSError object and returns this through its second argument. + + @param jsonText the json string to parse + @param error pointer to an NSError object to populate on error + + @return The NSArray or NSDictionary represented by the object, or nil if an error occured. + */ + +- (id)objectWithString:(NSString*)jsonText + error:(NSError**)error; + +@end + + diff --git a/SBJsonParser.m b/SBJsonParser.m new file mode 100755 index 0000000..d1b4b1e --- /dev/null +++ b/SBJsonParser.m @@ -0,0 +1,100 @@ +/* + Copyright (C) 2009,2010 Stig Brautaset. All rights reserved. + + 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 author nor the names of its contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + + 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. + */ + +#import "SBJsonParser.h" +#import "SBJsonStreamParser.h" +#import "SBJsonStreamParserAdapter.h" +#import "SBJsonStreamParserAccumulator.h" + +@implementation SBJsonParser + +@synthesize maxDepth; +@synthesize error; + +- (id)init { + self = [super init]; + if (self) + self.maxDepth = 32u; + return self; +} + + +#pragma mark Methods + +- (id)objectWithData:(NSData *)data { + + if (!data) { + self.error = @"Input was 'nil'"; + return nil; + } + + SBJsonStreamParserAccumulator *accumulator = [[SBJsonStreamParserAccumulator alloc] init]; + + SBJsonStreamParserAdapter *adapter = [[SBJsonStreamParserAdapter alloc] init]; + adapter.delegate = accumulator; + + SBJsonStreamParser *parser = [[SBJsonStreamParser alloc] init]; + parser.maxDepth = self.maxDepth; + parser.delegate = adapter; + + switch ([parser parse:data]) { + case SBJsonStreamParserComplete: + return accumulator.value; + break; + + case SBJsonStreamParserWaitingForData: + self.error = @"Unexpected end of input"; + break; + + case SBJsonStreamParserError: + self.error = parser.error; + break; + } + + return nil; +} + +- (id)objectWithString:(NSString *)repr { + return [self objectWithData:[repr dataUsingEncoding:NSUTF8StringEncoding]]; +} + +- (id)objectWithString:(NSString*)repr error:(NSError**)error_ { + id tmp = [self objectWithString:repr]; + if (tmp) + return tmp; + + if (error_) { + NSDictionary *ui = [NSDictionary dictionaryWithObjectsAndKeys:error, NSLocalizedDescriptionKey, nil]; + *error_ = [NSError errorWithDomain:@"org.brautaset.SBJsonParser.ErrorDomain" code:0 userInfo:ui]; + } + + return nil; +} + +@end diff --git a/SBJsonStreamParser.h b/SBJsonStreamParser.h new file mode 100755 index 0000000..2530eca --- /dev/null +++ b/SBJsonStreamParser.h @@ -0,0 +1,161 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + 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 the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + 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 + HOLDER 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. + */ + +#import + +@class SBJsonTokeniser; +@class SBJsonStreamParser; +@class SBJsonStreamParserState; + +typedef enum { + SBJsonStreamParserComplete, + SBJsonStreamParserWaitingForData, + SBJsonStreamParserError, +} SBJsonStreamParserStatus; + + +/** + @brief Delegate for interacting directly with the stream parser + + You will most likely find it much more convenient to implement the + SBJsonStreamParserAdapterDelegate protocol instead. + */ +@protocol SBJsonStreamParserDelegate + +/// Called when object start is found +- (void)parserFoundObjectStart:(SBJsonStreamParser*)parser; + +/// Called when object key is found +- (void)parser:(SBJsonStreamParser*)parser foundObjectKey:(NSString*)key; + +/// Called when object end is found +- (void)parserFoundObjectEnd:(SBJsonStreamParser*)parser; + +/// Called when array start is found +- (void)parserFoundArrayStart:(SBJsonStreamParser*)parser; + +/// Called when array end is found +- (void)parserFoundArrayEnd:(SBJsonStreamParser*)parser; + +/// Called when a boolean value is found +- (void)parser:(SBJsonStreamParser*)parser foundBoolean:(BOOL)x; + +/// Called when a null value is found +- (void)parserFoundNull:(SBJsonStreamParser*)parser; + +/// Called when a number is found +- (void)parser:(SBJsonStreamParser*)parser foundNumber:(NSNumber*)num; + +/// Called when a string is found +- (void)parser:(SBJsonStreamParser*)parser foundString:(NSString*)string; + +@end + + +/** + @brief Parse a stream of JSON data. + + Using this class directly you can reduce the apparent latency for each + download/parse cycle of documents over a slow connection. You can start + parsing *and return chunks of the parsed document* before the entire + document is downloaded. + + Using this class is also useful to parse huge documents on disk + bit by bit so you don't have to keep them all in memory. + + @see SBJsonStreamParserAdapter for more information. + + @see @ref objc2json + + */ +@interface SBJsonStreamParser : NSObject { +@private + SBJsonTokeniser *tokeniser; +} + +@property (nonatomic, unsafe_unretained) SBJsonStreamParserState *state; // Private +@property (nonatomic, readonly, strong) NSMutableArray *stateStack; // Private + +/** + @brief Expect multiple documents separated by whitespace + + Normally the @p -parse: method returns SBJsonStreamParserComplete when it's found a complete JSON document. + Attempting to parse any more data at that point is considered an error. ("Garbage after JSON".) + + If you set this property to true the parser will never return SBJsonStreamParserComplete. Rather, + once an object is completed it will expect another object to immediately follow, separated + only by (optional) whitespace. + + @see The TweetStream app in the Examples + */ +@property BOOL supportMultipleDocuments; + +/** + @brief Delegate to receive messages + + The object set here receives a series of messages as the parser breaks down the JSON stream + into valid tokens. + + @note + Usually this should be an instance of SBJsonStreamParserAdapter, but you can + substitute your own implementation of the SBJsonStreamParserDelegate protocol if you need to. + */ +@property (unsafe_unretained) id delegate; + +/** + @brief The max parse depth + + If the input is nested deeper than this the parser will halt parsing and return an error. + + Defaults to 32. + */ +@property NSUInteger maxDepth; + +/// Holds the error after SBJsonStreamParserError was returned +@property (copy) NSString *error; + +/** + @brief Parse some JSON + + The JSON is assumed to be UTF8 encoded. This can be a full JSON document, or a part of one. + + @param data An NSData object containing the next chunk of JSON + + @return + @li SBJsonStreamParserComplete if a full document was found + @li SBJsonStreamParserWaitingForData if a partial document was found and more data is required to complete it + @li SBJsonStreamParserError if an error occured. (See the error property for details in this case.) + + */ +- (SBJsonStreamParserStatus)parse:(NSData*)data; + +@end diff --git a/SBJsonStreamParser.m b/SBJsonStreamParser.m new file mode 100755 index 0000000..134e34a --- /dev/null +++ b/SBJsonStreamParser.m @@ -0,0 +1,255 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + 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 the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + 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 + HOLDER 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. + */ + +#import "SBJsonStreamParser.h" +#import "SBJsonTokeniser.h" +#import "SBJsonStreamParserState.h" +#import + +@implementation SBJsonStreamParser + +@synthesize supportMultipleDocuments; +@synthesize error; +@synthesize delegate; +@synthesize maxDepth; +@synthesize state; +@synthesize stateStack; + +#pragma mark Housekeeping + +- (id)init { + self = [super init]; + if (self) { + maxDepth = 32u; + stateStack = [[NSMutableArray alloc] initWithCapacity:maxDepth]; + state = [SBJsonStreamParserStateStart sharedInstance]; + tokeniser = [[SBJsonTokeniser alloc] init]; + } + return self; +} + +- (void)dealloc { + self.state = nil; +} + +#pragma mark Methods + +- (NSString*)tokenName:(sbjson_token_t)token { + switch (token) { + case sbjson_token_array_start: + return @"start of array"; + break; + + case sbjson_token_array_end: + return @"end of array"; + break; + + case sbjson_token_number: + return @"number"; + break; + + case sbjson_token_string: + return @"string"; + break; + + case sbjson_token_true: + case sbjson_token_false: + return @"boolean"; + break; + + case sbjson_token_null: + return @"null"; + break; + + case sbjson_token_keyval_separator: + return @"key-value separator"; + break; + + case sbjson_token_separator: + return @"value separator"; + break; + + case sbjson_token_object_start: + return @"start of object"; + break; + + case sbjson_token_object_end: + return @"end of object"; + break; + + case sbjson_token_eof: + case sbjson_token_error: + break; + } + NSAssert(NO, @"Should not get here"); + return @""; +} + +- (void)maxDepthError { + self.error = [NSString stringWithFormat:@"Input depth exceeds max depth of %lu", maxDepth]; + self.state = [SBJsonStreamParserStateError sharedInstance]; +} + +- (void)handleObjectStart { + if (stateStack.count >= maxDepth) { + [self maxDepthError]; + return; + } + + [delegate parserFoundObjectStart:self]; + [stateStack addObject:state]; + self.state = [SBJsonStreamParserStateObjectStart sharedInstance]; +} + +- (void)handleObjectEnd: (sbjson_token_t) tok { + self.state = [stateStack lastObject]; + [stateStack removeLastObject]; + [state parser:self shouldTransitionTo:tok]; + [delegate parserFoundObjectEnd:self]; +} + +- (void)handleArrayStart { + if (stateStack.count >= maxDepth) { + [self maxDepthError]; + return; + } + + [delegate parserFoundArrayStart:self]; + [stateStack addObject:state]; + self.state = [SBJsonStreamParserStateArrayStart sharedInstance]; +} + +- (void)handleArrayEnd: (sbjson_token_t) tok { + self.state = [stateStack lastObject]; + [stateStack removeLastObject]; + [state parser:self shouldTransitionTo:tok]; + [delegate parserFoundArrayEnd:self]; +} + +- (void) handleTokenNotExpectedHere: (sbjson_token_t) tok { + NSString *tokenName = [self tokenName:tok]; + NSString *stateName = [state name]; + + self.error = [NSString stringWithFormat:@"Token '%@' not expected %@", tokenName, stateName]; + self.state = [SBJsonStreamParserStateError sharedInstance]; +} + +- (SBJsonStreamParserStatus)parse:(NSData *)data_ { + @autoreleasepool { + [tokeniser appendData:data_]; + + for (;;) { + + if ([state isError]) + return SBJsonStreamParserError; + + NSObject *token; + sbjson_token_t tok = [tokeniser getToken:&token]; + switch (tok) { + case sbjson_token_eof: + return [state parserShouldReturn:self]; + break; + + case sbjson_token_error: + self.state = [SBJsonStreamParserStateError sharedInstance]; + self.error = tokeniser.error; + return SBJsonStreamParserError; + break; + + default: + + if (![state parser:self shouldAcceptToken:tok]) { + [self handleTokenNotExpectedHere: tok]; + return SBJsonStreamParserError; + } + + switch (tok) { + case sbjson_token_object_start: + [self handleObjectStart]; + break; + + case sbjson_token_object_end: + [self handleObjectEnd: tok]; + break; + + case sbjson_token_array_start: + [self handleArrayStart]; + break; + + case sbjson_token_array_end: + [self handleArrayEnd: tok]; + break; + + case sbjson_token_separator: + case sbjson_token_keyval_separator: + [state parser:self shouldTransitionTo:tok]; + break; + + case sbjson_token_true: + [delegate parser:self foundBoolean:YES]; + [state parser:self shouldTransitionTo:tok]; + break; + + case sbjson_token_false: + [delegate parser:self foundBoolean:NO]; + [state parser:self shouldTransitionTo:tok]; + break; + + case sbjson_token_null: + [delegate parserFoundNull:self]; + [state parser:self shouldTransitionTo:tok]; + break; + + case sbjson_token_number: + [delegate parser:self foundNumber:(NSNumber*)token]; + [state parser:self shouldTransitionTo:tok]; + break; + + case sbjson_token_string: + if ([state needKey]) + [delegate parser:self foundObjectKey:(NSString*)token]; + else + [delegate parser:self foundString:(NSString*)token]; + [state parser:self shouldTransitionTo:tok]; + break; + + default: + break; + } + break; + } + } + return SBJsonStreamParserComplete; + } +} + +@end diff --git a/SBJsonStreamParserAccumulator.h b/SBJsonStreamParserAccumulator.h new file mode 100755 index 0000000..141d6ee --- /dev/null +++ b/SBJsonStreamParserAccumulator.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2011 Stig Brautaset. All rights reserved. + + 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 author nor the names of its contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + + 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. + */ + +#import +#import "SBJsonStreamParserAdapter.h" + +@interface SBJsonStreamParserAccumulator : NSObject + +@property (copy) id value; + +@end diff --git a/SBJsonStreamParserAccumulator.m b/SBJsonStreamParserAccumulator.m new file mode 100755 index 0000000..1d39ceb --- /dev/null +++ b/SBJsonStreamParserAccumulator.m @@ -0,0 +1,47 @@ +/* + Copyright (C) 2011 Stig Brautaset. All rights reserved. + + 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 author nor the names of its contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + + 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. + */ + +#import "SBJsonStreamParserAccumulator.h" + +@implementation SBJsonStreamParserAccumulator + +@synthesize value; + + +#pragma mark SBJsonStreamParserAdapterDelegate + +- (void)parser:(SBJsonStreamParser*)parser foundArray:(NSArray *)array { + value = array; +} + +- (void)parser:(SBJsonStreamParser*)parser foundObject:(NSDictionary *)dict { + value = dict; +} + +@end diff --git a/SBJsonStreamParserAdapter.h b/SBJsonStreamParserAdapter.h new file mode 100755 index 0000000..942bc01 --- /dev/null +++ b/SBJsonStreamParserAdapter.h @@ -0,0 +1,148 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + 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 the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + 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 + HOLDER 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. + */ + +#import +#import "SBJsonStreamParser.h" + +typedef enum { + SBJsonStreamParserAdapterNone, + SBJsonStreamParserAdapterArray, + SBJsonStreamParserAdapterObject, +} SBJsonStreamParserAdapterType; + +/** + @brief Delegate for getting objects & arrays from the stream parser adapter + + @see The TweetStream example project. + */ +@protocol SBJsonStreamParserAdapterDelegate + +/** + @brief Called if a JSON array is found + + This method is called if a JSON array is found. + + */ +- (void)parser:(SBJsonStreamParser*)parser foundArray:(NSArray*)array; + +/** + @brief Called when a JSON object is found + + This method is called if a JSON object is found. + */ +- (void)parser:(SBJsonStreamParser*)parser foundObject:(NSDictionary*)dict; + +@end + +/** + @brief SBJsonStreamParserDelegate protocol adapter + + Rather than implementing the SBJsonStreamParserDelegate protocol yourself you will + most likely find it much more convenient to use an instance of this class and + implement the SBJsonStreamParserAdapterDelegate protocol instead. + + The default behaviour is that the delegate only receives one call from + either the -parser:foundArray: or -parser:foundObject: method when the + document is fully parsed. However, if your inputs contains multiple JSON + documents and you set the parser's -supportMultipleDocuments property to YES + you will get one call for each full method. + + @code + SBJsonStreamParserAdapter *adapter = [[[SBJsonStreamParserAdapter alloc] init] autorelease]; + adapter.delegate = self; + + SBJsonStreamParser *parser = [[[SBJsonStreamParser alloc] init] autorelease]; + parser.delegate = adapter; + parser.supportMultipleDocuments = YES; + + // Note that this input contains multiple top-level JSON documents + NSData *json = [@"[]{}[]{}" dataWithEncoding:NSUTF8StringEncoding]; + [parser parse:data]; + @endcode + + In the above example @p self will have the following sequence of methods called on it: + + @li -parser:foundArray: + @li -parser:foundObject: + @li -parser:foundArray: + @li -parser:foundObject: + + Often you won't have control over the input you're parsing, so can't make use of + this feature. But, all is not lost: this class will let you get the same effect by + allowing you to skip one or more of the outer enclosing objects. Thus, the next + example results in the same sequence of -parser:foundArray: / -parser:foundObject: + being called on your delegate. + + @code + SBJsonStreamParserAdapter *adapter = [[[SBJsonStreamParserAdapter alloc] init] autorelease]; + adapter.delegate = self; + adapter.levelsToSkip = 1; + + SBJsonStreamParser *parser = [[[SBJsonStreamParser alloc] init] autorelease]; + parser.delegate = adapter; + + // Note that this input contains A SINGLE top-level document + NSData *json = [@"[[],{},[],{}]" dataWithEncoding:NSUTF8StringEncoding]; + [parser parse:data]; + @endcode + +*/ +@interface SBJsonStreamParserAdapter : NSObject { +@private + NSUInteger depth; + NSMutableArray *array; + NSMutableDictionary *dict; + NSMutableArray *keyStack; + NSMutableArray *stack; + + SBJsonStreamParserAdapterType currentType; +} + +/** + @brief How many levels to skip + + This is useful for parsing huge JSON documents, or documents coming in over a very slow link. + + If you set this to N it will skip the outer N levels and call the -parser:foundArray: + or -parser:foundObject: methods for each of the inner objects, as appropriate. + + @see The StreamParserIntegrationTest.m file for examples +*/ +@property NSUInteger levelsToSkip; + +/** + @brief Your delegate object + Set this to the object you want to receive the SBJsonStreamParserAdapterDelegate messages. + */ +@property (unsafe_unretained) id delegate; + +@end diff --git a/SBJsonStreamParserAdapter.m b/SBJsonStreamParserAdapter.m new file mode 100755 index 0000000..e77b534 --- /dev/null +++ b/SBJsonStreamParserAdapter.m @@ -0,0 +1,164 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + 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 the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + 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 + HOLDER 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. + */ + +#import "SBJsonStreamParserAdapter.h" + +@interface SBJsonStreamParserAdapter () + +- (void)pop; +- (void)parser:(SBJsonStreamParser*)parser found:(id)obj; + +@end + + + +@implementation SBJsonStreamParserAdapter + +@synthesize delegate; +@synthesize levelsToSkip; + +#pragma mark Housekeeping + +- (id)init { + self = [super init]; + if (self) { + keyStack = [[NSMutableArray alloc] initWithCapacity:32]; + stack = [[NSMutableArray alloc] initWithCapacity:32]; + + currentType = SBJsonStreamParserAdapterNone; + } + return self; +} + + +#pragma mark Private methods + +- (void)pop { + [stack removeLastObject]; + array = nil; + dict = nil; + currentType = SBJsonStreamParserAdapterNone; + + id value = [stack lastObject]; + + if ([value isKindOfClass:[NSArray class]]) { + array = value; + currentType = SBJsonStreamParserAdapterArray; + } else if ([value isKindOfClass:[NSDictionary class]]) { + dict = value; + currentType = SBJsonStreamParserAdapterObject; + } +} + +- (void)parser:(SBJsonStreamParser*)parser found:(id)obj { + NSParameterAssert(obj); + + switch (currentType) { + case SBJsonStreamParserAdapterArray: + [array addObject:obj]; + break; + + case SBJsonStreamParserAdapterObject: + NSParameterAssert(keyStack.count); + [dict setObject:obj forKey:[keyStack lastObject]]; + [keyStack removeLastObject]; + break; + + case SBJsonStreamParserAdapterNone: + if ([obj isKindOfClass:[NSArray class]]) { + [delegate parser:parser foundArray:obj]; + } else { + [delegate parser:parser foundObject:obj]; + } + break; + + default: + break; + } +} + + +#pragma mark Delegate methods + +- (void)parserFoundObjectStart:(SBJsonStreamParser*)parser { + if (++depth > self.levelsToSkip) { + dict = [NSMutableDictionary new]; + [stack addObject:dict]; + currentType = SBJsonStreamParserAdapterObject; + } +} + +- (void)parser:(SBJsonStreamParser*)parser foundObjectKey:(NSString*)key_ { + [keyStack addObject:key_]; +} + +- (void)parserFoundObjectEnd:(SBJsonStreamParser*)parser { + if (depth-- > self.levelsToSkip) { + id value = dict; + [self pop]; + [self parser:parser found:value]; + } +} + +- (void)parserFoundArrayStart:(SBJsonStreamParser*)parser { + if (++depth > self.levelsToSkip) { + array = [NSMutableArray new]; + [stack addObject:array]; + currentType = SBJsonStreamParserAdapterArray; + } +} + +- (void)parserFoundArrayEnd:(SBJsonStreamParser*)parser { + if (depth-- > self.levelsToSkip) { + id value = array; + [self pop]; + [self parser:parser found:value]; + } +} + +- (void)parser:(SBJsonStreamParser*)parser foundBoolean:(BOOL)x { + [self parser:parser found:[NSNumber numberWithBool:x]]; +} + +- (void)parserFoundNull:(SBJsonStreamParser*)parser { + [self parser:parser found:[NSNull null]]; +} + +- (void)parser:(SBJsonStreamParser*)parser foundNumber:(NSNumber*)num { + [self parser:parser found:num]; +} + +- (void)parser:(SBJsonStreamParser*)parser foundString:(NSString*)string { + [self parser:parser found:string]; +} + +@end diff --git a/SBJsonStreamParserState.h b/SBJsonStreamParserState.h new file mode 100755 index 0000000..ea893cb --- /dev/null +++ b/SBJsonStreamParserState.h @@ -0,0 +1,83 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + 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 the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + 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 + HOLDER 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. + */ + +#import + +#import "SBJsonTokeniser.h" +#import "SBJsonStreamParser.h" + +@interface SBJsonStreamParserState : NSObject ++ (id)sharedInstance; + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token; +- (SBJsonStreamParserStatus)parserShouldReturn:(SBJsonStreamParser*)parser; +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok; +- (BOOL)needKey; +- (BOOL)isError; + +- (NSString*)name; + +@end + +@interface SBJsonStreamParserStateStart : SBJsonStreamParserState +@end + +@interface SBJsonStreamParserStateComplete : SBJsonStreamParserState +@end + +@interface SBJsonStreamParserStateError : SBJsonStreamParserState +@end + + +@interface SBJsonStreamParserStateObjectStart : SBJsonStreamParserState +@end + +@interface SBJsonStreamParserStateObjectGotKey : SBJsonStreamParserState +@end + +@interface SBJsonStreamParserStateObjectSeparator : SBJsonStreamParserState +@end + +@interface SBJsonStreamParserStateObjectGotValue : SBJsonStreamParserState +@end + +@interface SBJsonStreamParserStateObjectNeedKey : SBJsonStreamParserState +@end + +@interface SBJsonStreamParserStateArrayStart : SBJsonStreamParserState +@end + +@interface SBJsonStreamParserStateArrayGotValue : SBJsonStreamParserState +@end + +@interface SBJsonStreamParserStateArrayNeedValue : SBJsonStreamParserState +@end diff --git a/SBJsonStreamParserState.m b/SBJsonStreamParserState.m new file mode 100755 index 0000000..a24c6f6 --- /dev/null +++ b/SBJsonStreamParserState.m @@ -0,0 +1,355 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + 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 the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + 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 + HOLDER 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. + */ + +#import "SBJsonStreamParserState.h" +#import "SBJsonStreamParser.h" + +#define SINGLETON \ ++ (id)sharedInstance { \ + static id state; \ + if (!state) state = [[self alloc] init]; \ + return state; \ +} + +@implementation SBJsonStreamParserState + ++ (id)sharedInstance { return nil; } + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { + return NO; +} + +- (SBJsonStreamParserStatus)parserShouldReturn:(SBJsonStreamParser*)parser { + return SBJsonStreamParserWaitingForData; +} + +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok {} + +- (BOOL)needKey { + return NO; +} + +- (NSString*)name { + return @""; +} + +- (BOOL)isError { + return NO; +} + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateStart + +SINGLETON + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { + return token == sbjson_token_array_start || token == sbjson_token_object_start; +} + +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { + + SBJsonStreamParserState *state = nil; + switch (tok) { + case sbjson_token_array_start: + state = [SBJsonStreamParserStateArrayStart sharedInstance]; + break; + + case sbjson_token_object_start: + state = [SBJsonStreamParserStateObjectStart sharedInstance]; + break; + + case sbjson_token_array_end: + case sbjson_token_object_end: + if (parser.supportMultipleDocuments) + state = parser.state; + else + state = [SBJsonStreamParserStateComplete sharedInstance]; + break; + + case sbjson_token_eof: + return; + + default: + state = [SBJsonStreamParserStateError sharedInstance]; + break; + } + + + parser.state = state; +} + +- (NSString*)name { return @"before outer-most array or object"; } + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateComplete + +SINGLETON + +- (NSString*)name { return @"after outer-most array or object"; } + +- (SBJsonStreamParserStatus)parserShouldReturn:(SBJsonStreamParser*)parser { + return SBJsonStreamParserComplete; +} + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateError + +SINGLETON + +- (NSString*)name { return @"in error"; } + +- (SBJsonStreamParserStatus)parserShouldReturn:(SBJsonStreamParser*)parser { + return SBJsonStreamParserError; +} + +- (BOOL)isError { + return YES; +} + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateObjectStart + +SINGLETON + +- (NSString*)name { return @"at beginning of object"; } + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { + switch (token) { + case sbjson_token_object_end: + case sbjson_token_string: + return YES; + break; + default: + return NO; + break; + } +} + +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { + parser.state = [SBJsonStreamParserStateObjectGotKey sharedInstance]; +} + +- (BOOL)needKey { + return YES; +} + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateObjectGotKey + +SINGLETON + +- (NSString*)name { return @"after object key"; } + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { + return token == sbjson_token_keyval_separator; +} + +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { + parser.state = [SBJsonStreamParserStateObjectSeparator sharedInstance]; +} + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateObjectSeparator + +SINGLETON + +- (NSString*)name { return @"as object value"; } + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { + switch (token) { + case sbjson_token_object_start: + case sbjson_token_array_start: + case sbjson_token_true: + case sbjson_token_false: + case sbjson_token_null: + case sbjson_token_number: + case sbjson_token_string: + return YES; + break; + + default: + return NO; + break; + } +} + +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { + parser.state = [SBJsonStreamParserStateObjectGotValue sharedInstance]; +} + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateObjectGotValue + +SINGLETON + +- (NSString*)name { return @"after object value"; } + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { + switch (token) { + case sbjson_token_object_end: + case sbjson_token_separator: + return YES; + break; + default: + return NO; + break; + } +} + +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { + parser.state = [SBJsonStreamParserStateObjectNeedKey sharedInstance]; +} + + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateObjectNeedKey + +SINGLETON + +- (NSString*)name { return @"in place of object key"; } + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { + return sbjson_token_string == token; +} + +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { + parser.state = [SBJsonStreamParserStateObjectGotKey sharedInstance]; +} + +- (BOOL)needKey { + return YES; +} + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateArrayStart + +SINGLETON + +- (NSString*)name { return @"at array start"; } + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { + switch (token) { + case sbjson_token_object_end: + case sbjson_token_keyval_separator: + case sbjson_token_separator: + return NO; + break; + + default: + return YES; + break; + } +} + +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { + parser.state = [SBJsonStreamParserStateArrayGotValue sharedInstance]; +} + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateArrayGotValue + +SINGLETON + +- (NSString*)name { return @"after array value"; } + + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { + return token == sbjson_token_array_end || token == sbjson_token_separator; +} + +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { + if (tok == sbjson_token_separator) + parser.state = [SBJsonStreamParserStateArrayNeedValue sharedInstance]; +} + +@end + +#pragma mark - + +@implementation SBJsonStreamParserStateArrayNeedValue + +SINGLETON + +- (NSString*)name { return @"as array value"; } + + +- (BOOL)parser:(SBJsonStreamParser*)parser shouldAcceptToken:(sbjson_token_t)token { + switch (token) { + case sbjson_token_array_end: + case sbjson_token_keyval_separator: + case sbjson_token_object_end: + case sbjson_token_separator: + return NO; + break; + + default: + return YES; + break; + } +} + +- (void)parser:(SBJsonStreamParser*)parser shouldTransitionTo:(sbjson_token_t)tok { + parser.state = [SBJsonStreamParserStateArrayGotValue sharedInstance]; +} + +@end + diff --git a/SBJsonStreamWriter.h b/SBJsonStreamWriter.h new file mode 100755 index 0000000..9d14c5b --- /dev/null +++ b/SBJsonStreamWriter.h @@ -0,0 +1,188 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + 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 the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + 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 + HOLDER 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. + */ + +#import + +/// Enable JSON writing for non-native objects +@interface NSObject (SBProxyForJson) + +/** + @brief Allows generation of JSON for otherwise unsupported classes. + + If you have a custom class that you want to create a JSON representation + for you can implement this method in your class. It should return a + representation of your object defined in terms of objects that can be + translated into JSON. For example, a Person object might implement it like this: + + @code + - (id)proxyForJson { + return [NSDictionary dictionaryWithObjectsAndKeys: + name, @"name", + phone, @"phone", + email, @"email", + nil]; + } + @endcode + + */ +- (id)proxyForJson; + +@end + +@class SBJsonStreamWriter; + +@protocol SBJsonStreamWriterDelegate + +- (void)writer:(SBJsonStreamWriter*)writer appendBytes:(const void *)bytes length:(NSUInteger)length; + +@end + +@class SBJsonStreamWriterState; + +/** + @brief The Stream Writer class. + + Accepts a stream of messages and writes JSON of these to its delegate object. + + This class provides a range of high-, mid- and low-level methods. You can mix + and match calls to these. For example, you may want to call -writeArrayOpen + to start an array and then repeatedly call -writeObject: with various objects + before finishing off with a -writeArrayClose call. + + @see @ref json2objc + + */ + +@interface SBJsonStreamWriter : NSObject { + NSMutableDictionary *cache; +} + +@property (nonatomic, unsafe_unretained) SBJsonStreamWriterState *state; // Internal +@property (nonatomic, readonly, strong) NSMutableArray *stateStack; // Internal + +/** + @brief delegate to receive JSON output + Delegate that will receive messages with output. + */ +@property (unsafe_unretained) id delegate; + +/** + @brief The maximum recursing depth. + + Defaults to 512. If the input is nested deeper than this the input will be deemed to be + malicious and the parser returns nil, signalling an error. ("Nested too deep".) You can + turn off this security feature by setting the maxDepth value to 0. + */ +@property NSUInteger maxDepth; + +/** + @brief Whether we are generating human-readable (multiline) JSON. + + Set whether or not to generate human-readable JSON. The default is NO, which produces + JSON without any whitespace between tokens. If set to YES, generates human-readable + JSON with linebreaks after each array value and dictionary key/value pair, indented two + spaces per nesting level. + */ +@property BOOL humanReadable; + +/** + @brief Whether or not to sort the dictionary keys in the output. + + If this is set to YES, the dictionary keys in the JSON output will be in sorted order. + (This is useful if you need to compare two structures, for example.) The default is NO. + */ +@property BOOL sortKeys; + +/// Contains the error description after an error has occured. +@property (copy) NSString *error; + +/** + Write an NSDictionary to the JSON stream. + @return YES if successful, or NO on failure + */ +- (BOOL)writeObject:(NSDictionary*)dict; + +/** + Write an NSArray to the JSON stream. + @return YES if successful, or NO on failure + */ +- (BOOL)writeArray:(NSArray *)array; + +/** + Start writing an Object to the stream + @return YES if successful, or NO on failure +*/ +- (BOOL)writeObjectOpen; + +/** + Close the current object being written + @return YES if successful, or NO on failure +*/ +- (BOOL)writeObjectClose; + +/** Start writing an Array to the stream + @return YES if successful, or NO on failure +*/ +- (BOOL)writeArrayOpen; + +/** Close the current Array being written + @return YES if successful, or NO on failure +*/ +- (BOOL)writeArrayClose; + +/** Write a null to the stream + @return YES if successful, or NO on failure +*/ +- (BOOL)writeNull; + +/** Write a boolean to the stream + @return YES if successful, or NO on failure +*/ +- (BOOL)writeBool:(BOOL)x; + +/** Write a Number to the stream + @return YES if successful, or NO on failure +*/ +- (BOOL)writeNumber:(NSNumber*)n; + +/** Write a String to the stream + @return YES if successful, or NO on failure +*/ +- (BOOL)writeString:(NSString*)s; + +@end + +@interface SBJsonStreamWriter (Private) +- (BOOL)writeValue:(id)v; +- (void)appendBytes:(const void *)bytes length:(NSUInteger)length; +@end + diff --git a/SBJsonStreamWriter.m b/SBJsonStreamWriter.m new file mode 100755 index 0000000..2851d6a --- /dev/null +++ b/SBJsonStreamWriter.m @@ -0,0 +1,366 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + 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 the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + 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 + HOLDER 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. + */ + +#import "SBJsonStreamWriter.h" +#import "SBJsonStreamWriterState.h" + +static NSNumber *kNotANumber; +static NSNumber *kTrue; +static NSNumber *kFalse; +static NSNumber *kPositiveInfinity; +static NSNumber *kNegativeInfinity; + + +@implementation SBJsonStreamWriter + +@synthesize error; +@synthesize maxDepth; +@synthesize state; +@synthesize stateStack; +@synthesize humanReadable; +@synthesize sortKeys; + ++ (void)initialize { + kNotANumber = [NSDecimalNumber notANumber]; + kPositiveInfinity = [NSNumber numberWithDouble:+INFINITY]; + kNegativeInfinity = [NSNumber numberWithDouble:-INFINITY]; + kTrue = [NSNumber numberWithBool:YES]; + kFalse = [NSNumber numberWithBool:NO]; +} + +#pragma mark Housekeeping + +@synthesize delegate; + +- (id)init { + self = [super init]; + if (self) { + maxDepth = 32u; + stateStack = [[NSMutableArray alloc] initWithCapacity:maxDepth]; + state = [SBJsonStreamWriterStateStart sharedInstance]; + cache = [[NSMutableDictionary alloc] initWithCapacity:32]; + } + return self; +} + +- (void)dealloc { + self.state = nil; +} + +#pragma mark Methods + +- (void)appendBytes:(const void *)bytes length:(NSUInteger)length { + [delegate writer:self appendBytes:bytes length:length]; +} + +- (BOOL)writeObject:(NSDictionary *)dict { + if (![self writeObjectOpen]) + return NO; + + NSArray *keys = [dict allKeys]; + if (sortKeys) + keys = [keys sortedArrayUsingSelector:@selector(compare:)]; + + for (id k in keys) { + if (![k isKindOfClass:[NSString class]]) { + self.error = [NSString stringWithFormat:@"JSON object key must be string: %@", k]; + return NO; + } + + if (![self writeString:k]) + return NO; + if (![self writeValue:[dict objectForKey:k]]) + return NO; + } + + return [self writeObjectClose]; +} + +- (BOOL)writeArray:(NSArray*)array { + if (![self writeArrayOpen]) + return NO; + for (id v in array) + if (![self writeValue:v]) + return NO; + return [self writeArrayClose]; +} + + +- (BOOL)writeObjectOpen { + if ([state isInvalidState:self]) return NO; + if ([state expectingKey:self]) return NO; + [state appendSeparator:self]; + if (humanReadable && stateStack.count) [state appendWhitespace:self]; + + [stateStack addObject:state]; + self.state = [SBJsonStreamWriterStateObjectStart sharedInstance]; + + if (maxDepth && stateStack.count > maxDepth) { + self.error = @"Nested too deep"; + return NO; + } + + [delegate writer:self appendBytes:"{" length:1]; + return YES; +} + +- (BOOL)writeObjectClose { + if ([state isInvalidState:self]) return NO; + + SBJsonStreamWriterState *prev = state; + + self.state = [stateStack lastObject]; + [stateStack removeLastObject]; + + if (humanReadable) [prev appendWhitespace:self]; + [delegate writer:self appendBytes:"}" length:1]; + + [state transitionState:self]; + return YES; +} + +- (BOOL)writeArrayOpen { + if ([state isInvalidState:self]) return NO; + if ([state expectingKey:self]) return NO; + [state appendSeparator:self]; + if (humanReadable && stateStack.count) [state appendWhitespace:self]; + + [stateStack addObject:state]; + self.state = [SBJsonStreamWriterStateArrayStart sharedInstance]; + + if (maxDepth && stateStack.count > maxDepth) { + self.error = @"Nested too deep"; + return NO; + } + + [delegate writer:self appendBytes:"[" length:1]; + return YES; +} + +- (BOOL)writeArrayClose { + if ([state isInvalidState:self]) return NO; + if ([state expectingKey:self]) return NO; + + SBJsonStreamWriterState *prev = state; + + self.state = [stateStack lastObject]; + [stateStack removeLastObject]; + + if (humanReadable) [prev appendWhitespace:self]; + [delegate writer:self appendBytes:"]" length:1]; + + [state transitionState:self]; + return YES; +} + +- (BOOL)writeNull { + if ([state isInvalidState:self]) return NO; + if ([state expectingKey:self]) return NO; + [state appendSeparator:self]; + if (humanReadable) [state appendWhitespace:self]; + + [delegate writer:self appendBytes:"null" length:4]; + [state transitionState:self]; + return YES; +} + +- (BOOL)writeBool:(BOOL)x { + if ([state isInvalidState:self]) return NO; + if ([state expectingKey:self]) return NO; + [state appendSeparator:self]; + if (humanReadable) [state appendWhitespace:self]; + + if (x) + [delegate writer:self appendBytes:"true" length:4]; + else + [delegate writer:self appendBytes:"false" length:5]; + [state transitionState:self]; + return YES; +} + + +- (BOOL)writeValue:(id)o { + if ([o isKindOfClass:[NSDictionary class]]) { + return [self writeObject:o]; + + } else if ([o isKindOfClass:[NSArray class]]) { + return [self writeArray:o]; + + } else if ([o isKindOfClass:[NSString class]]) { + [self writeString:o]; + return YES; + + } else if ([o isKindOfClass:[NSNumber class]]) { + return [self writeNumber:o]; + + } else if ([o isKindOfClass:[NSNull class]]) { + return [self writeNull]; + + } else if ([o respondsToSelector:@selector(proxyForJson)]) { + return [self writeValue:[o proxyForJson]]; + + } + + self.error = [NSString stringWithFormat:@"JSON serialisation not supported for %@", [o class]]; + return NO; +} + +static const char *strForChar(int c) { + switch (c) { + case 0: return "\\u0000"; break; + case 1: return "\\u0001"; break; + case 2: return "\\u0002"; break; + case 3: return "\\u0003"; break; + case 4: return "\\u0004"; break; + case 5: return "\\u0005"; break; + case 6: return "\\u0006"; break; + case 7: return "\\u0007"; break; + case 8: return "\\b"; break; + case 9: return "\\t"; break; + case 10: return "\\n"; break; + case 11: return "\\u000b"; break; + case 12: return "\\f"; break; + case 13: return "\\r"; break; + case 14: return "\\u000e"; break; + case 15: return "\\u000f"; break; + case 16: return "\\u0010"; break; + case 17: return "\\u0011"; break; + case 18: return "\\u0012"; break; + case 19: return "\\u0013"; break; + case 20: return "\\u0014"; break; + case 21: return "\\u0015"; break; + case 22: return "\\u0016"; break; + case 23: return "\\u0017"; break; + case 24: return "\\u0018"; break; + case 25: return "\\u0019"; break; + case 26: return "\\u001a"; break; + case 27: return "\\u001b"; break; + case 28: return "\\u001c"; break; + case 29: return "\\u001d"; break; + case 30: return "\\u001e"; break; + case 31: return "\\u001f"; break; + case 34: return "\\\""; break; + case 92: return "\\\\"; break; + } + NSLog(@"FUTFUTFUT: -->'%c'<---", c); + return "FUTFUTFUT"; +} + +- (BOOL)writeString:(NSString*)string { + if ([state isInvalidState:self]) return NO; + [state appendSeparator:self]; + if (humanReadable) [state appendWhitespace:self]; + + NSMutableData *buf = [cache objectForKey:string]; + if (!buf) { + + NSUInteger len = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + const char *utf8 = [string UTF8String]; + NSUInteger written = 0, i = 0; + + buf = [NSMutableData dataWithCapacity:(NSUInteger)(len * 1.1f)]; + [buf appendBytes:"\"" length:1]; + + for (i = 0; i < len; i++) { + int c = utf8[i]; + BOOL isControlChar = c >= 0 && c < 32; + if (isControlChar || c == '"' || c == '\\') { + if (i - written) + [buf appendBytes:utf8 + written length:i - written]; + written = i + 1; + + const char *t = strForChar(c); + [buf appendBytes:t length:strlen(t)]; + } + } + + if (i - written) + [buf appendBytes:utf8 + written length:i - written]; + + [buf appendBytes:"\"" length:1]; + [cache setObject:buf forKey:string]; + } + + [delegate writer:self appendBytes:[buf bytes] length:[buf length]]; + [state transitionState:self]; + return YES; +} + +- (BOOL)writeNumber:(NSNumber*)number { + if (number == kTrue || number == kFalse) + return [self writeBool:[number boolValue]]; + + if ([state isInvalidState:self]) return NO; + if ([state expectingKey:self]) return NO; + [state appendSeparator:self]; + if (humanReadable) [state appendWhitespace:self]; + + if ([kPositiveInfinity isEqualToNumber:number]) { + self.error = @"+Infinity is not a valid number in JSON"; + return NO; + + } else if ([kNegativeInfinity isEqualToNumber:number]) { + self.error = @"-Infinity is not a valid number in JSON"; + return NO; + + } else if ([kNotANumber isEqualToNumber:number]) { + self.error = @"NaN is not a valid number in JSON"; + return NO; + } + + const char *objcType = [number objCType]; + char num[128]; + size_t len; + + switch (objcType[0]) { + case 'c': case 'i': case 's': case 'l': case 'q': + len = snprintf(num, sizeof num, "%lld", [number longLongValue]); + break; + case 'C': case 'I': case 'S': case 'L': case 'Q': + len = snprintf(num, sizeof num, "%llu", [number unsignedLongLongValue]); + break; + case 'f': case 'd': default: + if ([number isKindOfClass:[NSDecimalNumber class]]) { + char const *utf8 = [[number stringValue] UTF8String]; + [delegate writer:self appendBytes:utf8 length: strlen(utf8)]; + [state transitionState:self]; + return YES; + } + len = snprintf(num, sizeof num, "%.17g", [number doubleValue]); + break; + } + [delegate writer:self appendBytes:num length: len]; + [state transitionState:self]; + return YES; +} + +@end diff --git a/SBJsonStreamWriterAccumulator.h b/SBJsonStreamWriterAccumulator.h new file mode 100755 index 0000000..b12d0d5 --- /dev/null +++ b/SBJsonStreamWriterAccumulator.h @@ -0,0 +1,36 @@ +/* + Copyright (C) 2011 Stig Brautaset. All rights reserved. + + 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 author nor the names of its contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + + 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. + */ + +#import "SBJsonStreamWriter.h" + +@interface SBJsonStreamWriterAccumulator : NSObject + +@property (readonly, copy) NSMutableData* data; + +@end diff --git a/SBJsonStreamWriterAccumulator.m b/SBJsonStreamWriterAccumulator.m new file mode 100755 index 0000000..be65190 --- /dev/null +++ b/SBJsonStreamWriterAccumulator.m @@ -0,0 +1,52 @@ +/* + Copyright (C) 2011 Stig Brautaset. All rights reserved. + + 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 author nor the names of its contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + + 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. + */ + +#import "SBJsonStreamWriterAccumulator.h" + + +@implementation SBJsonStreamWriterAccumulator + +@synthesize data; + +- (id)init { + self = [super init]; + if (self) { + data = [[NSMutableData alloc] initWithCapacity:8096u]; + } + return self; +} + + +#pragma mark SBJsonStreamWriterDelegate + +- (void)writer:(SBJsonStreamWriter *)writer appendBytes:(const void *)bytes length:(NSUInteger)length { + [data appendBytes:bytes length:length]; +} + +@end diff --git a/SBJsonStreamWriterState.h b/SBJsonStreamWriterState.h new file mode 100755 index 0000000..90d442a --- /dev/null +++ b/SBJsonStreamWriterState.h @@ -0,0 +1,69 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + 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 the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + 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 + HOLDER 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. + */ + +#import + +@class SBJsonStreamWriter; + +@interface SBJsonStreamWriterState : NSObject ++ (id)sharedInstance; +- (BOOL)isInvalidState:(SBJsonStreamWriter*)writer; +- (void)appendSeparator:(SBJsonStreamWriter*)writer; +- (BOOL)expectingKey:(SBJsonStreamWriter*)writer; +- (void)transitionState:(SBJsonStreamWriter*)writer; +- (void)appendWhitespace:(SBJsonStreamWriter*)writer; +@end + +@interface SBJsonStreamWriterStateObjectStart : SBJsonStreamWriterState +@end + +@interface SBJsonStreamWriterStateObjectKey : SBJsonStreamWriterStateObjectStart +@end + +@interface SBJsonStreamWriterStateObjectValue : SBJsonStreamWriterState +@end + +@interface SBJsonStreamWriterStateArrayStart : SBJsonStreamWriterState +@end + +@interface SBJsonStreamWriterStateArrayValue : SBJsonStreamWriterState +@end + +@interface SBJsonStreamWriterStateStart : SBJsonStreamWriterState +@end + +@interface SBJsonStreamWriterStateComplete : SBJsonStreamWriterState +@end + +@interface SBJsonStreamWriterStateError : SBJsonStreamWriterState +@end + diff --git a/SBJsonStreamWriterState.m b/SBJsonStreamWriterState.m new file mode 100755 index 0000000..9f04cac --- /dev/null +++ b/SBJsonStreamWriterState.m @@ -0,0 +1,139 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + 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 the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + 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 + HOLDER 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. + */ + +#import "SBJsonStreamWriterState.h" +#import "SBJsonStreamWriter.h" + +#define SINGLETON \ ++ (id)sharedInstance { \ + static id state; \ + if (!state) state = [[self alloc] init]; \ + return state; \ +} + + +@implementation SBJsonStreamWriterState ++ (id)sharedInstance { return nil; } +- (BOOL)isInvalidState:(SBJsonStreamWriter*)writer { return NO; } +- (void)appendSeparator:(SBJsonStreamWriter*)writer {} +- (BOOL)expectingKey:(SBJsonStreamWriter*)writer { return NO; } +- (void)transitionState:(SBJsonStreamWriter *)writer {} +- (void)appendWhitespace:(SBJsonStreamWriter*)writer { + [writer appendBytes:"\n" length:1]; + for (NSUInteger i = 0; i < writer.stateStack.count; i++) + [writer appendBytes:" " length:2]; +} +@end + +@implementation SBJsonStreamWriterStateObjectStart + +SINGLETON + +- (void)transitionState:(SBJsonStreamWriter *)writer { + writer.state = [SBJsonStreamWriterStateObjectValue sharedInstance]; +} +- (BOOL)expectingKey:(SBJsonStreamWriter *)writer { + writer.error = @"JSON object key must be string"; + return YES; +} +@end + +@implementation SBJsonStreamWriterStateObjectKey + +SINGLETON + +- (void)appendSeparator:(SBJsonStreamWriter *)writer { + [writer appendBytes:"," length:1]; +} +@end + +@implementation SBJsonStreamWriterStateObjectValue + +SINGLETON + +- (void)appendSeparator:(SBJsonStreamWriter *)writer { + [writer appendBytes:":" length:1]; +} +- (void)transitionState:(SBJsonStreamWriter *)writer { + writer.state = [SBJsonStreamWriterStateObjectKey sharedInstance]; +} +- (void)appendWhitespace:(SBJsonStreamWriter *)writer { + [writer appendBytes:" " length:1]; +} +@end + +@implementation SBJsonStreamWriterStateArrayStart + +SINGLETON + +- (void)transitionState:(SBJsonStreamWriter *)writer { + writer.state = [SBJsonStreamWriterStateArrayValue sharedInstance]; +} +@end + +@implementation SBJsonStreamWriterStateArrayValue + +SINGLETON + +- (void)appendSeparator:(SBJsonStreamWriter *)writer { + [writer appendBytes:"," length:1]; +} +@end + +@implementation SBJsonStreamWriterStateStart + +SINGLETON + + +- (void)transitionState:(SBJsonStreamWriter *)writer { + writer.state = [SBJsonStreamWriterStateComplete sharedInstance]; +} +- (void)appendSeparator:(SBJsonStreamWriter *)writer { +} +@end + +@implementation SBJsonStreamWriterStateComplete + +SINGLETON + +- (BOOL)isInvalidState:(SBJsonStreamWriter*)writer { + writer.error = @"Stream is closed"; + return YES; +} +@end + +@implementation SBJsonStreamWriterStateError + +SINGLETON + +@end + diff --git a/SBJsonTokeniser.h b/SBJsonTokeniser.h new file mode 100755 index 0000000..e484a94 --- /dev/null +++ b/SBJsonTokeniser.h @@ -0,0 +1,67 @@ +/* + Copyright (c) 2010, Stig Brautaset. + All rights reserved. + + 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 the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + 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 + HOLDER 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. + */ + +#import + +typedef enum { + sbjson_token_error = -1, + sbjson_token_eof, + + sbjson_token_array_start, + sbjson_token_array_end, + + sbjson_token_object_start, + sbjson_token_object_end, + + sbjson_token_separator, + sbjson_token_keyval_separator, + + sbjson_token_number, + sbjson_token_string, + sbjson_token_true, + sbjson_token_false, + sbjson_token_null, + +} sbjson_token_t; + +@class SBJsonUTF8Stream; + +@interface SBJsonTokeniser : NSObject + +@property (strong) SBJsonUTF8Stream *stream; +@property (copy) NSString *error; + +- (void)appendData:(NSData*)data_; + +- (sbjson_token_t)getToken:(NSObject**)token; + +@end diff --git a/SBJsonTokeniser.m b/SBJsonTokeniser.m new file mode 100755 index 0000000..75d3268 --- /dev/null +++ b/SBJsonTokeniser.m @@ -0,0 +1,453 @@ +/* + Copyright (c) 2010-2011, Stig Brautaset. All rights reserved. + + 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 the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + 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 + HOLDER 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. + */ + +#import "SBJsonTokeniser.h" +#import "SBJsonUTF8Stream.h" + +#define SBStringIsIllegalSurrogateHighCharacter(character) (((character) >= 0xD800UL) && ((character) <= 0xDFFFUL)) +#define SBStringIsSurrogateLowCharacter(character) ((character >= 0xDC00UL) && (character <= 0xDFFFUL)) +#define SBStringIsSurrogateHighCharacter(character) ((character >= 0xD800UL) && (character <= 0xDBFFUL)) + +@implementation SBJsonTokeniser + +@synthesize error = _error; +@synthesize stream = _stream; + +- (id)init { + self = [super init]; + if (self) { + _stream = [[SBJsonUTF8Stream alloc] init]; + + } + + return self; +} + + +- (void)appendData:(NSData *)data_ { + [_stream appendData:data_]; +} + + +- (sbjson_token_t)match:(const char *)pattern length:(NSUInteger)len retval:(sbjson_token_t)token { + if (![_stream haveRemainingCharacters:len]) + return sbjson_token_eof; + + if ([_stream skipCharacters:pattern length:len]) + return token; + + self.error = [NSString stringWithFormat:@"Expected '%s' after initial '%.1s'", pattern, pattern]; + return sbjson_token_error; +} + +- (BOOL)decodeEscape:(unichar)ch into:(unichar*)decoded { + switch (ch) { + case '\\': + case '/': + case '"': + *decoded = ch; + break; + + case 'b': + *decoded = '\b'; + break; + + case 'n': + *decoded = '\n'; + break; + + case 'r': + *decoded = '\r'; + break; + + case 't': + *decoded = '\t'; + break; + + case 'f': + *decoded = '\f'; + break; + + default: + self.error = @"Illegal escape character"; + return NO; + break; + } + return YES; +} + +- (BOOL)decodeHexQuad:(unichar*)quad { + unichar c, tmp = 0; + + for (int i = 0; i < 4; i++) { + (void)[_stream getNextUnichar:&c]; + tmp *= 16; + switch (c) { + case '0' ... '9': + tmp += c - '0'; + break; + + case 'a' ... 'f': + tmp += 10 + c - 'a'; + break; + + case 'A' ... 'F': + tmp += 10 + c - 'A'; + break; + + default: + return NO; + } + } + *quad = tmp; + return YES; +} + +- (sbjson_token_t)getStringToken:(NSObject**)token { + NSMutableString *acc = nil; + + for (;;) { + [_stream skip]; + + unichar ch; + { + NSMutableString *string = nil; + + if (![_stream getStringFragment:&string]) + return sbjson_token_eof; + + if (!string) { + self.error = @"Broken Unicode encoding"; + return sbjson_token_error; + } + + if (![_stream getUnichar:&ch]) + return sbjson_token_eof; + + if (acc) { + [acc appendString:string]; + + } else if (ch == '"') { + *token = [string copy]; + [_stream skip]; + return sbjson_token_string; + + } else { + acc = [string mutableCopy]; + } + } + + + switch (ch) { + case 0 ... 0x1F: + self.error = [NSString stringWithFormat:@"Unescaped control character [0x%0.2X]", (int)ch]; + return sbjson_token_error; + break; + + case '"': + *token = acc; + [_stream skip]; + return sbjson_token_string; + break; + + case '\\': + if (![_stream getNextUnichar:&ch]) + return sbjson_token_eof; + + if (ch == 'u') { + if (![_stream haveRemainingCharacters:5]) + return sbjson_token_eof; + + unichar hi; + if (![self decodeHexQuad:&hi]) { + self.error = @"Invalid hex quad"; + return sbjson_token_error; + } + + if (SBStringIsSurrogateHighCharacter(hi)) { + unichar lo; + + if (![_stream haveRemainingCharacters:6]) + return sbjson_token_eof; + + (void)[_stream getNextUnichar:&ch]; + (void)[_stream getNextUnichar:&lo]; + if (ch != '\\' || lo != 'u' || ![self decodeHexQuad:&lo]) { + self.error = @"Missing low character in surrogate pair"; + return sbjson_token_error; + } + + if (!SBStringIsSurrogateLowCharacter(lo)) { + self.error = @"Invalid low character in surrogate pair"; + return sbjson_token_error; + } + + [acc appendFormat:@"%C%C", hi, lo]; + } else if (SBStringIsIllegalSurrogateHighCharacter(hi)) { + self.error = @"Invalid high character in surrogate pair"; + return sbjson_token_error; + } else { + [acc appendFormat:@"%C", hi]; + } + + + } else { + unichar decoded; + if (![self decodeEscape:ch into:&decoded]) + return sbjson_token_error; + [acc appendFormat:@"%C", decoded]; + } + + break; + + default: { + self.error = [NSString stringWithFormat:@"Invalid UTF-8: '%x'", (int)ch]; + return sbjson_token_error; + break; + } + } + } + return sbjson_token_eof; +} + +- (sbjson_token_t)getNumberToken:(NSObject**)token { + + NSUInteger numberStart = _stream.index; + NSCharacterSet *digits = [NSCharacterSet decimalDigitCharacterSet]; + + unichar ch; + if (![_stream getUnichar:&ch]) + return sbjson_token_eof; + + BOOL isNegative = NO; + if (ch == '-') { + isNegative = YES; + if (![_stream getNextUnichar:&ch]) + return sbjson_token_eof; + } + + unsigned long long mantissa = 0; + int mantissa_length = 0; + + if (ch == '0') { + mantissa_length++; + if (![_stream getNextUnichar:&ch]) + return sbjson_token_eof; + + if ([digits characterIsMember:ch]) { + self.error = @"Leading zero is illegal in number"; + return sbjson_token_error; + } + } + + while ([digits characterIsMember:ch]) { + mantissa *= 10; + mantissa += (ch - '0'); + mantissa_length++; + + if (![_stream getNextUnichar:&ch]) + return sbjson_token_eof; + } + + short exponent = 0; + BOOL isFloat = NO; + + if (ch == '.') { + isFloat = YES; + if (![_stream getNextUnichar:&ch]) + return sbjson_token_eof; + + while ([digits characterIsMember:ch]) { + mantissa *= 10; + mantissa += (ch - '0'); + mantissa_length++; + exponent--; + + if (![_stream getNextUnichar:&ch]) + return sbjson_token_eof; + } + + if (!exponent) { + self.error = @"No digits after decimal point"; + return sbjson_token_error; + } + } + + BOOL hasExponent = NO; + if (ch == 'e' || ch == 'E') { + hasExponent = YES; + + if (![_stream getNextUnichar:&ch]) + return sbjson_token_eof; + + BOOL expIsNegative = NO; + if (ch == '-') { + expIsNegative = YES; + if (![_stream getNextUnichar:&ch]) + return sbjson_token_eof; + + } else if (ch == '+') { + if (![_stream getNextUnichar:&ch]) + return sbjson_token_eof; + } + + short explicit_exponent = 0; + short explicit_exponent_length = 0; + while ([digits characterIsMember:ch]) { + explicit_exponent *= 10; + explicit_exponent += (ch - '0'); + explicit_exponent_length++; + + if (![_stream getNextUnichar:&ch]) + return sbjson_token_eof; + } + + if (explicit_exponent_length == 0) { + self.error = @"No digits in exponent"; + return sbjson_token_error; + } + + if (expIsNegative) + exponent -= explicit_exponent; + else + exponent += explicit_exponent; + } + + if (!mantissa_length && isNegative) { + self.error = @"No digits after initial minus"; + return sbjson_token_error; + + } else if (mantissa_length >= 19) { + + NSString *number = [_stream stringWithRange:NSMakeRange(numberStart, _stream.index - numberStart)]; + *token = [NSDecimalNumber decimalNumberWithString:number]; + + } else if (!isFloat && !hasExponent) { + if (!isNegative) + *token = [NSNumber numberWithUnsignedLongLong:mantissa]; + else + *token = [NSNumber numberWithLongLong:-mantissa]; + } else { + *token = [NSDecimalNumber decimalNumberWithMantissa:mantissa + exponent:exponent + isNegative:isNegative]; + } + + return sbjson_token_number; +} + +- (sbjson_token_t)getToken:(NSObject **)token { + + [_stream skipWhitespace]; + + unichar ch; + if (![_stream getUnichar:&ch]) + return sbjson_token_eof; + + NSUInteger oldIndexLocation = _stream.index; + sbjson_token_t tok; + + switch (ch) { + case '[': + tok = sbjson_token_array_start; + [_stream skip]; + break; + + case ']': + tok = sbjson_token_array_end; + [_stream skip]; + break; + + case '{': + tok = sbjson_token_object_start; + [_stream skip]; + break; + + case ':': + tok = sbjson_token_keyval_separator; + [_stream skip]; + break; + + case '}': + tok = sbjson_token_object_end; + [_stream skip]; + break; + + case ',': + tok = sbjson_token_separator; + [_stream skip]; + break; + + case 'n': + tok = [self match:"null" length:4 retval:sbjson_token_null]; + break; + + case 't': + tok = [self match:"true" length:4 retval:sbjson_token_true]; + break; + + case 'f': + tok = [self match:"false" length:5 retval:sbjson_token_false]; + break; + + case '"': + tok = [self getStringToken:token]; + break; + + case '0' ... '9': + case '-': + tok = [self getNumberToken:token]; + break; + + case '+': + self.error = @"Leading + is illegal in number"; + tok = sbjson_token_error; + break; + + default: + self.error = [NSString stringWithFormat:@"Illegal start of token [%c]", ch]; + tok = sbjson_token_error; + break; + } + + if (tok == sbjson_token_eof) { + // We ran out of bytes in the middle of a token. + // We don't know how to restart in mid-flight, so + // rewind to the start of the token for next attempt. + // Hopefully we'll have more data then. + _stream.index = oldIndexLocation; + } + + return tok; +} + + +@end diff --git a/SBJsonUTF8Stream.h b/SBJsonUTF8Stream.h new file mode 100755 index 0000000..a26f032 --- /dev/null +++ b/SBJsonUTF8Stream.h @@ -0,0 +1,58 @@ +/* + Copyright (c) 2011, Stig Brautaset. All rights reserved. + + 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 the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + 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 + HOLDER 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. + */ + +#import + + +@interface SBJsonUTF8Stream : NSObject { +@private + const char *_bytes; + NSMutableData *_data; + NSUInteger _length; +} + +@property (assign) NSUInteger index; + +- (void)appendData:(NSData*)data_; + +- (BOOL)haveRemainingCharacters:(NSUInteger)chars; + +- (void)skip; +- (void)skipWhitespace; +- (BOOL)skipCharacters:(const char *)chars length:(NSUInteger)len; + +- (BOOL)getUnichar:(unichar*)ch; +- (BOOL)getNextUnichar:(unichar*)ch; +- (BOOL)getStringFragment:(NSString**)string; + +- (NSString*)stringWithRange:(NSRange)range; + +@end diff --git a/SBJsonUTF8Stream.m b/SBJsonUTF8Stream.m new file mode 100755 index 0000000..f57015d --- /dev/null +++ b/SBJsonUTF8Stream.m @@ -0,0 +1,141 @@ +/* + Copyright (c) 2011, Stig Brautaset. All rights reserved. + + 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 the author nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + 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 + HOLDER 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. + */ + +#import "SBJsonUTF8Stream.h" + + +@implementation SBJsonUTF8Stream + +@synthesize index = _index; + +- (id)init { + self = [super init]; + if (self) { + _data = [[NSMutableData alloc] initWithCapacity:4096u]; + } + return self; +} + + +- (void)appendData:(NSData *)data_ { + + if (_index) { + // Discard data we've already parsed + [_data replaceBytesInRange:NSMakeRange(0, _index) withBytes:"" length:0]; + + // Reset index to point to current position + _index = 0; + } + + [_data appendData:data_]; + + // This is an optimisation. + _bytes = (const char*)[_data bytes]; + _length = [_data length]; +} + + +- (BOOL)getUnichar:(unichar*)ch { + if (_index < _length) { + *ch = (unichar)_bytes[_index]; + return YES; + } + return NO; +} + +- (BOOL)getNextUnichar:(unichar*)ch { + if (++_index < _length) { + *ch = (unichar)_bytes[_index]; + return YES; + } + return NO; +} + +- (BOOL)getStringFragment:(NSString **)string { + NSUInteger start = _index; + while (_index < _length) { + switch (_bytes[_index]) { + case '"': + case '\\': + case 0 ... 0x1f: + *string = [[NSString alloc] initWithBytes:(_bytes + start) + length:(_index - start) + encoding:NSUTF8StringEncoding]; + return YES; + break; + default: + _index++; + break; + } + } + return NO; +} + +- (void)skip { + _index++; +} + +- (void)skipWhitespace { + while (_index < _length) { + switch (_bytes[_index]) { + case ' ': + case '\t': + case '\r': + case '\n': + _index++; + break; + default: + return; + break; + } + } +} + +- (BOOL)haveRemainingCharacters:(NSUInteger)chars { + return [_data length] - _index >= chars; +} + +- (BOOL)skipCharacters:(const char *)chars length:(NSUInteger)len { + const void *bytes = ((const char*)[_data bytes]) + _index; + if (!memcmp(bytes, chars, len)) { + _index += len; + return YES; + } + return NO; +} + +- (NSString*)stringWithRange:(NSRange)range { + return [[NSString alloc] initWithBytes:_bytes + range.location length:range.length encoding:NSUTF8StringEncoding]; + +} + + +@end diff --git a/SBJsonWriter.h b/SBJsonWriter.h new file mode 100755 index 0000000..b0ae5e9 --- /dev/null +++ b/SBJsonWriter.h @@ -0,0 +1,110 @@ +/* + Copyright (C) 2009 Stig Brautaset. All rights reserved. + + 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 author nor the names of its contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + + 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. + */ + +#import + +/** + @brief The JSON writer class. + + This uses SBJsonStreamWriter internally. + + @see @ref json2objc + */ + +@interface SBJsonWriter : NSObject + +/** + @brief The maximum recursing depth. + + Defaults to 32. If the input is nested deeper than this the input will be deemed to be + malicious and the parser returns nil, signalling an error. ("Nested too deep".) You can + turn off this security feature by setting the maxDepth value to 0. + */ +@property NSUInteger maxDepth; + +/** + @brief Return an error trace, or nil if there was no errors. + + Note that this method returns the trace of the last method that failed. + You need to check the return value of the call you're making to figure out + if the call actually failed, before you know call this method. + */ +@property (readonly, copy) NSString *error; + +/** + @brief Whether we are generating human-readable (multiline) JSON. + + Set whether or not to generate human-readable JSON. The default is NO, which produces + JSON without any whitespace. (Except inside strings.) If set to YES, generates human-readable + JSON with linebreaks after each array value and dictionary key/value pair, indented two + spaces per nesting level. + */ +@property BOOL humanReadable; + +/** + @brief Whether or not to sort the dictionary keys in the output. + + If this is set to YES, the dictionary keys in the JSON output will be in sorted order. + (This is useful if you need to compare two structures, for example.) The default is NO. + */ +@property BOOL sortKeys; + +/** + @brief Return JSON representation for the given object. + + Returns a string containing JSON representation of the passed in value, or nil on error. + If nil is returned and @p error is not NULL, @p *error can be interrogated to find the cause of the error. + + @param value any instance that can be represented as JSON text. + */ +- (NSString*)stringWithObject:(id)value; + +/** + @brief Return JSON representation for the given object. + + Returns an NSData object containing JSON represented as UTF8 text, or nil on error. + + @param value any instance that can be represented as JSON text. + */ +- (NSData*)dataWithObject:(id)value; + +/** + @brief Return JSON representation (or fragment) for the given object. + + Returns a string containing JSON representation of the passed in value, or nil on error. + If nil is returned and @p error is not NULL, @p *error can be interrogated to find the cause of the error. + + @param value any instance that can be represented as a JSON fragment + @param error pointer to object to be populated with NSError on failure + + */- (NSString*)stringWithObject:(id)value + error:(NSError**)error; + + +@end diff --git a/SBJsonWriter.m b/SBJsonWriter.m new file mode 100755 index 0000000..854da97 --- /dev/null +++ b/SBJsonWriter.m @@ -0,0 +1,109 @@ +/* + Copyright (C) 2009 Stig Brautaset. All rights reserved. + + 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 author nor the names of its contributors may be used + to endorse or promote products derived from this software without specific + prior written permission. + + 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. + */ + +#import "SBJsonWriter.h" +#import "SBJsonStreamWriter.h" +#import "SBJsonStreamWriterAccumulator.h" + + +@interface SBJsonWriter () +@property (copy) NSString *error; +@end + +@implementation SBJsonWriter + +@synthesize sortKeys; +@synthesize humanReadable; + +@synthesize error; +@synthesize maxDepth; + +- (id)init { + self = [super init]; + if (self) { + self.maxDepth = 32u; + } + return self; +} + + +- (NSString*)stringWithObject:(id)value { + NSData *data = [self dataWithObject:value]; + if (data) + return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + return nil; +} + +- (NSString*)stringWithObject:(id)value error:(NSError**)error_ { + NSString *tmp = [self stringWithObject:value]; + if (tmp) + return tmp; + + if (error_) { + NSDictionary *ui = [NSDictionary dictionaryWithObjectsAndKeys:error, NSLocalizedDescriptionKey, nil]; + *error_ = [NSError errorWithDomain:@"org.brautaset.SBJsonWriter.ErrorDomain" code:0 userInfo:ui]; + } + + return nil; +} + +- (NSData*)dataWithObject:(id)object { + self.error = nil; + + SBJsonStreamWriterAccumulator *accumulator = [[SBJsonStreamWriterAccumulator alloc] init]; + + SBJsonStreamWriter *streamWriter = [[SBJsonStreamWriter alloc] init]; + streamWriter.sortKeys = self.sortKeys; + streamWriter.maxDepth = self.maxDepth; + streamWriter.humanReadable = self.humanReadable; + streamWriter.delegate = accumulator; + + BOOL ok = NO; + if ([object isKindOfClass:[NSDictionary class]]) + ok = [streamWriter writeObject:object]; + + else if ([object isKindOfClass:[NSArray class]]) + ok = [streamWriter writeArray:object]; + + else if ([object respondsToSelector:@selector(proxyForJson)]) + return [self dataWithObject:[object proxyForJson]]; + else { + self.error = @"Not valid type for JSON"; + return nil; + } + + if (ok) + return accumulator.data; + + self.error = streamWriter.error; + return nil; +} + + +@end