├── README.md ├── javadocset.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── javadocset.xccheckout │ └── xcuserdata │ │ └── bogdan.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── bogdan.xcuserdatad │ └── xcschemes │ ├── javadocset.xcscheme │ └── xcschememanagement.plist └── javadocset ├── DHIndexer.h ├── DHIndexer.m ├── FMDatabase.h ├── FMDatabase.m ├── FMDatabaseAdditions.h ├── FMDatabaseAdditions.m ├── FMDatabasePool.h ├── FMDatabasePool.m ├── FMDatabaseQueue.h ├── FMDatabaseQueue.m ├── FMResultSet.h ├── FMResultSet.m ├── LICENSE.txt ├── javadocset-Prefix.pch ├── javadocset.1 └── main.m /README.md: -------------------------------------------------------------------------------- 1 | javadocset 2 | ========== 3 | 4 | Simple command line tool for generating a Dash docset from Javadoc-generated documentation 5 | 6 | ## Usage (macOS) 7 | 8 | 1. Download a compiled binary from https://kapeli.com/javadocset.zip or compile it yourself from source using Xcode. 9 | 2. Use it: ```./javadocset ``` 10 | 11 | Example usage: ```./javadocset Java api``` 12 | 13 | ## Linux, Windows and other platforms 14 | 15 | Use https://github.com/william8th/javadocset. 16 | -------------------------------------------------------------------------------- /javadocset.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 431B5B00171E6D4300868D3A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 431B5AFF171E6D4300868D3A /* Foundation.framework */; }; 11 | 431B5B03171E6D4300868D3A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 431B5B02171E6D4300868D3A /* main.m */; }; 12 | 431B5B07171E6D4300868D3A /* javadocset.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 431B5B06171E6D4300868D3A /* javadocset.1 */; }; 13 | 431B5B0E171E6D6F00868D3A /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 431B5B0D171E6D6F00868D3A /* AppKit.framework */; }; 14 | 431B5B10171E6D7400868D3A /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 431B5B0F171E6D7400868D3A /* WebKit.framework */; }; 15 | 431B5B13171E6DC300868D3A /* DHIndexer.m in Sources */ = {isa = PBXBuildFile; fileRef = 431B5B12171E6DC300868D3A /* DHIndexer.m */; }; 16 | 4381865D171FD821002D1BC6 /* FMDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = 43818653171FD821002D1BC6 /* FMDatabase.m */; }; 17 | 4381865E171FD821002D1BC6 /* FMDatabaseAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 43818655171FD821002D1BC6 /* FMDatabaseAdditions.m */; }; 18 | 4381865F171FD821002D1BC6 /* FMDatabasePool.m in Sources */ = {isa = PBXBuildFile; fileRef = 43818657171FD821002D1BC6 /* FMDatabasePool.m */; }; 19 | 43818660171FD821002D1BC6 /* FMDatabaseQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 43818659171FD821002D1BC6 /* FMDatabaseQueue.m */; }; 20 | 43818662171FD821002D1BC6 /* FMResultSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 4381865C171FD821002D1BC6 /* FMResultSet.m */; }; 21 | 43818664171FD834002D1BC6 /* libsqlite3.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 43818663171FD834002D1BC6 /* libsqlite3.0.dylib */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXCopyFilesBuildPhase section */ 25 | 431B5AFA171E6D4300868D3A /* CopyFiles */ = { 26 | isa = PBXCopyFilesBuildPhase; 27 | buildActionMask = 2147483647; 28 | dstPath = /usr/share/man/man1/; 29 | dstSubfolderSpec = 0; 30 | files = ( 31 | 431B5B07171E6D4300868D3A /* javadocset.1 in CopyFiles */, 32 | ); 33 | runOnlyForDeploymentPostprocessing = 1; 34 | }; 35 | /* End PBXCopyFilesBuildPhase section */ 36 | 37 | /* Begin PBXFileReference section */ 38 | 431B5AFC171E6D4300868D3A /* javadocset */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = javadocset; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | 431B5AFF171E6D4300868D3A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 40 | 431B5B02171E6D4300868D3A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 41 | 431B5B05171E6D4300868D3A /* javadocset-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "javadocset-Prefix.pch"; sourceTree = ""; }; 42 | 431B5B06171E6D4300868D3A /* javadocset.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = javadocset.1; sourceTree = ""; }; 43 | 431B5B0D171E6D6F00868D3A /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 44 | 431B5B0F171E6D7400868D3A /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; 45 | 431B5B11171E6DC300868D3A /* DHIndexer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DHIndexer.h; sourceTree = ""; }; 46 | 431B5B12171E6DC300868D3A /* DHIndexer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DHIndexer.m; sourceTree = ""; }; 47 | 43818652171FD821002D1BC6 /* FMDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabase.h; sourceTree = ""; }; 48 | 43818653171FD821002D1BC6 /* FMDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabase.m; sourceTree = ""; }; 49 | 43818654171FD821002D1BC6 /* FMDatabaseAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabaseAdditions.h; sourceTree = ""; }; 50 | 43818655171FD821002D1BC6 /* FMDatabaseAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabaseAdditions.m; sourceTree = ""; }; 51 | 43818656171FD821002D1BC6 /* FMDatabasePool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabasePool.h; sourceTree = ""; }; 52 | 43818657171FD821002D1BC6 /* FMDatabasePool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabasePool.m; sourceTree = ""; }; 53 | 43818658171FD821002D1BC6 /* FMDatabaseQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabaseQueue.h; sourceTree = ""; }; 54 | 43818659171FD821002D1BC6 /* FMDatabaseQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabaseQueue.m; sourceTree = ""; }; 55 | 4381865B171FD821002D1BC6 /* FMResultSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMResultSet.h; sourceTree = ""; }; 56 | 4381865C171FD821002D1BC6 /* FMResultSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMResultSet.m; sourceTree = ""; }; 57 | 43818663171FD834002D1BC6 /* libsqlite3.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.0.dylib; path = usr/lib/libsqlite3.0.dylib; sourceTree = SDKROOT; }; 58 | 43818665171FD867002D1BC6 /* LICENSE.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE.txt; sourceTree = ""; }; 59 | /* End PBXFileReference section */ 60 | 61 | /* Begin PBXFrameworksBuildPhase section */ 62 | 431B5AF9171E6D4300868D3A /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | 43818664171FD834002D1BC6 /* libsqlite3.0.dylib in Frameworks */, 67 | 431B5B10171E6D7400868D3A /* WebKit.framework in Frameworks */, 68 | 431B5B0E171E6D6F00868D3A /* AppKit.framework in Frameworks */, 69 | 431B5B00171E6D4300868D3A /* Foundation.framework in Frameworks */, 70 | ); 71 | runOnlyForDeploymentPostprocessing = 0; 72 | }; 73 | /* End PBXFrameworksBuildPhase section */ 74 | 75 | /* Begin PBXGroup section */ 76 | 431B5AF3171E6D4300868D3A = { 77 | isa = PBXGroup; 78 | children = ( 79 | 431B5B01171E6D4300868D3A /* javadocset */, 80 | 431B5AFE171E6D4300868D3A /* Frameworks */, 81 | 431B5AFD171E6D4300868D3A /* Products */, 82 | ); 83 | sourceTree = ""; 84 | }; 85 | 431B5AFD171E6D4300868D3A /* Products */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 431B5AFC171E6D4300868D3A /* javadocset */, 89 | ); 90 | name = Products; 91 | sourceTree = ""; 92 | }; 93 | 431B5AFE171E6D4300868D3A /* Frameworks */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 43818663171FD834002D1BC6 /* libsqlite3.0.dylib */, 97 | 431B5B0F171E6D7400868D3A /* WebKit.framework */, 98 | 431B5B0D171E6D6F00868D3A /* AppKit.framework */, 99 | 431B5AFF171E6D4300868D3A /* Foundation.framework */, 100 | ); 101 | name = Frameworks; 102 | sourceTree = ""; 103 | }; 104 | 431B5B01171E6D4300868D3A /* javadocset */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 43818651171FD812002D1BC6 /* FMDB */, 108 | 431B5B02171E6D4300868D3A /* main.m */, 109 | 431B5B11171E6DC300868D3A /* DHIndexer.h */, 110 | 431B5B12171E6DC300868D3A /* DHIndexer.m */, 111 | 431B5B06171E6D4300868D3A /* javadocset.1 */, 112 | 431B5B04171E6D4300868D3A /* Supporting Files */, 113 | ); 114 | path = javadocset; 115 | sourceTree = ""; 116 | }; 117 | 431B5B04171E6D4300868D3A /* Supporting Files */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 431B5B05171E6D4300868D3A /* javadocset-Prefix.pch */, 121 | ); 122 | name = "Supporting Files"; 123 | sourceTree = ""; 124 | }; 125 | 43818651171FD812002D1BC6 /* FMDB */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 43818652171FD821002D1BC6 /* FMDatabase.h */, 129 | 43818653171FD821002D1BC6 /* FMDatabase.m */, 130 | 43818654171FD821002D1BC6 /* FMDatabaseAdditions.h */, 131 | 43818655171FD821002D1BC6 /* FMDatabaseAdditions.m */, 132 | 43818656171FD821002D1BC6 /* FMDatabasePool.h */, 133 | 43818657171FD821002D1BC6 /* FMDatabasePool.m */, 134 | 43818658171FD821002D1BC6 /* FMDatabaseQueue.h */, 135 | 43818659171FD821002D1BC6 /* FMDatabaseQueue.m */, 136 | 4381865B171FD821002D1BC6 /* FMResultSet.h */, 137 | 4381865C171FD821002D1BC6 /* FMResultSet.m */, 138 | 43818665171FD867002D1BC6 /* LICENSE.txt */, 139 | ); 140 | name = FMDB; 141 | sourceTree = ""; 142 | }; 143 | /* End PBXGroup section */ 144 | 145 | /* Begin PBXNativeTarget section */ 146 | 431B5AFB171E6D4300868D3A /* javadocset */ = { 147 | isa = PBXNativeTarget; 148 | buildConfigurationList = 431B5B0A171E6D4300868D3A /* Build configuration list for PBXNativeTarget "javadocset" */; 149 | buildPhases = ( 150 | 431B5AF8171E6D4300868D3A /* Sources */, 151 | 431B5AF9171E6D4300868D3A /* Frameworks */, 152 | 431B5AFA171E6D4300868D3A /* CopyFiles */, 153 | ); 154 | buildRules = ( 155 | ); 156 | dependencies = ( 157 | ); 158 | name = javadocset; 159 | productName = javadocset; 160 | productReference = 431B5AFC171E6D4300868D3A /* javadocset */; 161 | productType = "com.apple.product-type.tool"; 162 | }; 163 | /* End PBXNativeTarget section */ 164 | 165 | /* Begin PBXProject section */ 166 | 431B5AF4171E6D4300868D3A /* Project object */ = { 167 | isa = PBXProject; 168 | attributes = { 169 | LastUpgradeCheck = 0460; 170 | ORGANIZATIONNAME = Kapeli; 171 | }; 172 | buildConfigurationList = 431B5AF7171E6D4300868D3A /* Build configuration list for PBXProject "javadocset" */; 173 | compatibilityVersion = "Xcode 3.2"; 174 | developmentRegion = English; 175 | hasScannedForEncodings = 0; 176 | knownRegions = ( 177 | en, 178 | ); 179 | mainGroup = 431B5AF3171E6D4300868D3A; 180 | productRefGroup = 431B5AFD171E6D4300868D3A /* Products */; 181 | projectDirPath = ""; 182 | projectRoot = ""; 183 | targets = ( 184 | 431B5AFB171E6D4300868D3A /* javadocset */, 185 | ); 186 | }; 187 | /* End PBXProject section */ 188 | 189 | /* Begin PBXSourcesBuildPhase section */ 190 | 431B5AF8171E6D4300868D3A /* Sources */ = { 191 | isa = PBXSourcesBuildPhase; 192 | buildActionMask = 2147483647; 193 | files = ( 194 | 431B5B03171E6D4300868D3A /* main.m in Sources */, 195 | 431B5B13171E6DC300868D3A /* DHIndexer.m in Sources */, 196 | 4381865D171FD821002D1BC6 /* FMDatabase.m in Sources */, 197 | 4381865E171FD821002D1BC6 /* FMDatabaseAdditions.m in Sources */, 198 | 4381865F171FD821002D1BC6 /* FMDatabasePool.m in Sources */, 199 | 43818660171FD821002D1BC6 /* FMDatabaseQueue.m in Sources */, 200 | 43818662171FD821002D1BC6 /* FMResultSet.m in Sources */, 201 | ); 202 | runOnlyForDeploymentPostprocessing = 0; 203 | }; 204 | /* End PBXSourcesBuildPhase section */ 205 | 206 | /* Begin XCBuildConfiguration section */ 207 | 431B5B08171E6D4300868D3A /* Debug */ = { 208 | isa = XCBuildConfiguration; 209 | buildSettings = { 210 | ALWAYS_SEARCH_USER_PATHS = NO; 211 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 212 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 213 | CLANG_CXX_LIBRARY = "libc++"; 214 | CLANG_ENABLE_OBJC_ARC = YES; 215 | CLANG_WARN_CONSTANT_CONVERSION = YES; 216 | CLANG_WARN_EMPTY_BODY = YES; 217 | CLANG_WARN_ENUM_CONVERSION = YES; 218 | CLANG_WARN_INT_CONVERSION = YES; 219 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 220 | COPY_PHASE_STRIP = NO; 221 | GCC_C_LANGUAGE_STANDARD = gnu99; 222 | GCC_DYNAMIC_NO_PIC = NO; 223 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 224 | GCC_OPTIMIZATION_LEVEL = 0; 225 | GCC_PREPROCESSOR_DEFINITIONS = ( 226 | "DEBUG=1", 227 | "$(inherited)", 228 | ); 229 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 230 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 231 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 232 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 233 | GCC_WARN_UNUSED_VARIABLE = YES; 234 | MACOSX_DEPLOYMENT_TARGET = 10.8; 235 | ONLY_ACTIVE_ARCH = YES; 236 | SDKROOT = macosx; 237 | }; 238 | name = Debug; 239 | }; 240 | 431B5B09171E6D4300868D3A /* Release */ = { 241 | isa = XCBuildConfiguration; 242 | buildSettings = { 243 | ALWAYS_SEARCH_USER_PATHS = NO; 244 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 245 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 246 | CLANG_CXX_LIBRARY = "libc++"; 247 | CLANG_ENABLE_OBJC_ARC = YES; 248 | CLANG_WARN_CONSTANT_CONVERSION = YES; 249 | CLANG_WARN_EMPTY_BODY = YES; 250 | CLANG_WARN_ENUM_CONVERSION = YES; 251 | CLANG_WARN_INT_CONVERSION = YES; 252 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 253 | COPY_PHASE_STRIP = YES; 254 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 255 | GCC_C_LANGUAGE_STANDARD = gnu99; 256 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 257 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 258 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 259 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 260 | GCC_WARN_UNUSED_VARIABLE = YES; 261 | MACOSX_DEPLOYMENT_TARGET = 10.8; 262 | SDKROOT = macosx; 263 | }; 264 | name = Release; 265 | }; 266 | 431B5B0B171E6D4300868D3A /* Debug */ = { 267 | isa = XCBuildConfiguration; 268 | buildSettings = { 269 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 270 | GCC_PREFIX_HEADER = "javadocset/javadocset-Prefix.pch"; 271 | MACOSX_DEPLOYMENT_TARGET = 10.6; 272 | PRODUCT_NAME = "$(TARGET_NAME)"; 273 | }; 274 | name = Debug; 275 | }; 276 | 431B5B0C171E6D4300868D3A /* Release */ = { 277 | isa = XCBuildConfiguration; 278 | buildSettings = { 279 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 280 | GCC_PREFIX_HEADER = "javadocset/javadocset-Prefix.pch"; 281 | MACOSX_DEPLOYMENT_TARGET = 10.6; 282 | PRODUCT_NAME = "$(TARGET_NAME)"; 283 | }; 284 | name = Release; 285 | }; 286 | /* End XCBuildConfiguration section */ 287 | 288 | /* Begin XCConfigurationList section */ 289 | 431B5AF7171E6D4300868D3A /* Build configuration list for PBXProject "javadocset" */ = { 290 | isa = XCConfigurationList; 291 | buildConfigurations = ( 292 | 431B5B08171E6D4300868D3A /* Debug */, 293 | 431B5B09171E6D4300868D3A /* Release */, 294 | ); 295 | defaultConfigurationIsVisible = 0; 296 | defaultConfigurationName = Release; 297 | }; 298 | 431B5B0A171E6D4300868D3A /* Build configuration list for PBXNativeTarget "javadocset" */ = { 299 | isa = XCConfigurationList; 300 | buildConfigurations = ( 301 | 431B5B0B171E6D4300868D3A /* Debug */, 302 | 431B5B0C171E6D4300868D3A /* Release */, 303 | ); 304 | defaultConfigurationIsVisible = 0; 305 | defaultConfigurationName = Release; 306 | }; 307 | /* End XCConfigurationList section */ 308 | }; 309 | rootObject = 431B5AF4171E6D4300868D3A /* Project object */; 310 | } 311 | -------------------------------------------------------------------------------- /javadocset.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /javadocset.xcodeproj/project.xcworkspace/xcshareddata/javadocset.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 3B9A7EAD-4237-4DC3-97BD-31606C5AE73A 9 | IDESourceControlProjectName 10 | javadocset 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | E730A8C8D6CA67DF28251A4E7BC0DF447D42AB07 14 | https://github.com/Kapeli/javadocset.git 15 | 16 | IDESourceControlProjectPath 17 | javadocset.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | E730A8C8D6CA67DF28251A4E7BC0DF447D42AB07 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/Kapeli/javadocset.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | E730A8C8D6CA67DF28251A4E7BC0DF447D42AB07 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | E730A8C8D6CA67DF28251A4E7BC0DF447D42AB07 36 | IDESourceControlWCCName 37 | javadocset 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /javadocset.xcodeproj/project.xcworkspace/xcuserdata/bogdan.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kapeli/javadocset/4cc6afe4c137828847cd8c5473c66925c2bc6dc3/javadocset.xcodeproj/project.xcworkspace/xcuserdata/bogdan.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /javadocset.xcodeproj/xcuserdata/bogdan.xcuserdatad/xcschemes/javadocset.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /javadocset.xcodeproj/xcuserdata/bogdan.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | javadocset.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 431B5AFB171E6D4300868D3A 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /javadocset/DHIndexer.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "FMDatabase.h" 4 | #import "FMDatabaseAdditions.h" 5 | 6 | @interface DHIndexer : NSObject 7 | 8 | @property (retain) NSString *apiPath; 9 | @property (retain) NSString *workingDir; 10 | @property (retain) NSString *docsetName; 11 | @property (retain) NSString *docsetPath; 12 | 13 | @property (retain) NSString *contentsDir; 14 | @property (retain) NSString *resourcesDir; 15 | @property (retain) NSString *documentsDir; 16 | 17 | @property (assign) BOOL hasMultipleIndexes; 18 | @property (retain) NSMutableArray *toIndex; 19 | 20 | @property (retain) WebView *webView; 21 | @property (retain) NSMutableArray *added; 22 | @property (retain) FMDatabase *db; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /javadocset/DHIndexer.m: -------------------------------------------------------------------------------- 1 | #import "DHIndexer.h" 2 | 3 | @implementation DHIndexer 4 | 5 | - (void)startIndexing 6 | { 7 | printf("Start indexing...\n"); 8 | self.added = [NSMutableArray array]; 9 | self.webView = [[WebView alloc] init]; 10 | [self initDB]; 11 | [self.webView setFrameLoadDelegate:self]; 12 | [self step]; 13 | } 14 | 15 | - (void)step 16 | { 17 | if(!self.toIndex.count) 18 | { 19 | [self.db commit]; 20 | [self.db close]; 21 | printf("All done!\n"); 22 | exit(0); 23 | } 24 | else 25 | { 26 | NSString *next = [self.toIndex objectAtIndex:0]; 27 | printf("Indexing %s...", [[next lastPathComponent] UTF8String]); 28 | [self.toIndex removeObjectAtIndex:0]; 29 | [self.webView setMainFrameURL:next]; 30 | } 31 | } 32 | 33 | - (void)parseEntries 34 | { 35 | DOMDocument *document = [self.webView mainFrameDocument]; 36 | DOMNodeList *anchors = [document getElementsByTagName:@"a"]; 37 | int count = 0; 38 | for(int i = 0; i < anchors.length; i++) 39 | { 40 | DOMHTMLAnchorElement *anchor = (DOMHTMLAnchorElement*)[anchors item:i]; 41 | DOMHTMLElement *parent = (DOMHTMLElement*)[anchor parentElement]; 42 | if([parent firstChild] != anchor) 43 | { 44 | continue; 45 | } 46 | if([[parent tagName] isCaseInsensitiveLike:@"span"] || [[parent tagName] isCaseInsensitiveLike:@"code"] || [[parent tagName] isCaseInsensitiveLike:@"i"] || [[parent tagName] isCaseInsensitiveLike:@"b"]) 47 | { 48 | parent = (DOMHTMLElement*)[parent parentElement]; 49 | if([parent firstChild] != [anchor parentElement]) 50 | { 51 | continue; 52 | } 53 | } 54 | if(![[parent tagName] isCaseInsensitiveLike:@"dt"]) 55 | { 56 | continue; 57 | } 58 | NSString *text = [parent textContent]; 59 | NSString *type = nil; 60 | NSString *name = [anchor textContent]; 61 | NSString *dtClassName = [parent className]; 62 | dtClassName = (dtClassName) ? dtClassName : @""; 63 | if([text rangeOfString:@"Search tag in" options:NSCaseInsensitiveSearch].location != NSNotFound || [text rangeOfString:@"- search tag" options:NSCaseInsensitiveSearch].location != NSNotFound) 64 | { 65 | type = @"Section"; 66 | } 67 | else if([text rangeOfString:@"Class in" options:NSCaseInsensitiveSearch].location != NSNotFound || [text rangeOfString:@"- class" options:NSCaseInsensitiveSearch].location != NSNotFound || [dtClassName hasSuffix:@"class"]) 68 | { 69 | type = @"Class"; 70 | } 71 | else if([text rangeOfString:@"Static method in" options:NSCaseInsensitiveSearch].location != NSNotFound || [dtClassName hasSuffix:@"method"]) 72 | { 73 | type = @"Method"; 74 | } 75 | else if([text rangeOfString:@"Static variable in" options:NSCaseInsensitiveSearch].location != NSNotFound || [dtClassName hasSuffix:@"field"] || [text rangeOfString:@"Field in" options:NSCaseInsensitiveSearch].location != NSNotFound) 76 | { 77 | type = @"Field"; 78 | } 79 | else if([text rangeOfString:@"Constructor" options:NSCaseInsensitiveSearch].location != NSNotFound || [dtClassName hasSuffix:@"constructor"]) 80 | { 81 | type = @"Constructor"; 82 | } 83 | else if([text rangeOfString:@"Method in" options:NSCaseInsensitiveSearch].location != NSNotFound) 84 | { 85 | type = @"Method"; 86 | } 87 | else if([text rangeOfString:@"Variable in" options:NSCaseInsensitiveSearch].location != NSNotFound) 88 | { 89 | type = @"Field"; 90 | } 91 | else if([text rangeOfString:@"Interface in" options:NSCaseInsensitiveSearch].location != NSNotFound || [text rangeOfString:@"- interface" options:NSCaseInsensitiveSearch].location != NSNotFound || [dtClassName hasSuffix:@"interface"]) 92 | { 93 | type = @"Interface"; 94 | } 95 | else if([text rangeOfString:@"Exception in" options:NSCaseInsensitiveSearch].location != NSNotFound || [text rangeOfString:@"- exception" options:NSCaseInsensitiveSearch].location != NSNotFound || [dtClassName hasSuffix:@"exception"]) 96 | { 97 | type = @"Exception"; 98 | } 99 | else if([text rangeOfString:@"Error in" options:NSCaseInsensitiveSearch].location != NSNotFound || [text rangeOfString:@"- error" options:NSCaseInsensitiveSearch].location != NSNotFound || [dtClassName hasSuffix:@"error"]) 100 | { 101 | type = @"Error"; 102 | } 103 | else if([text rangeOfString:@"Enum in" options:NSCaseInsensitiveSearch].location != NSNotFound || [text rangeOfString:@"- enum" options:NSCaseInsensitiveSearch].location != NSNotFound || [dtClassName hasSuffix:@"enum"]) 104 | { 105 | type = @"Enum"; 106 | } 107 | else if([text rangeOfString:@"Trait in" options:NSCaseInsensitiveSearch].location != NSNotFound) 108 | { 109 | type = @"Trait"; 110 | } 111 | else if([text rangeOfString:@"Script in" options:NSCaseInsensitiveSearch].location != NSNotFound) 112 | { 113 | type = @"Script"; 114 | } 115 | else if([text rangeOfString:@"Annotation Type" options:NSCaseInsensitiveSearch].location != NSNotFound || [dtClassName hasSuffix:@"annotation"]) 116 | { 117 | type = @"Notation"; 118 | } 119 | else if([text rangeOfString:@"package" options:NSCaseInsensitiveSearch].location != NSNotFound || [dtClassName hasSuffix:@"package"]) 120 | { 121 | type = @"Package"; 122 | } 123 | else 124 | { 125 | printf("\nWarning: could not determine type for %s. Please tell the developer about this!\n", [name UTF8String]); 126 | printf("\n%s and %s\n", [text UTF8String], [dtClassName UTF8String]); 127 | continue; 128 | } 129 | NSString *path = [[anchor absoluteLinkURL] absoluteString]; 130 | NSRange baseRange = [path rangeOfString:@".docset/Contents/Resources/Documents/" options:NSBackwardsSearch]; 131 | if(baseRange.location != NSNotFound) 132 | { 133 | path = [path substringFromIndex:baseRange.location+baseRange.length]; 134 | [self insertName:name type:type path:path]; 135 | } 136 | ++count; 137 | } 138 | printf("added %d entries\n", count); 139 | } 140 | 141 | - (void)webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame 142 | { 143 | printf("failed to load page\n"); 144 | [self step]; 145 | } 146 | 147 | - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame 148 | { 149 | if(frame == [self.webView mainFrame]) 150 | { 151 | [self parseEntries]; 152 | [self step]; 153 | } 154 | } 155 | 156 | - (void)insertName:(NSString *)name type:(NSString *)type path:(NSString *)path 157 | { 158 | if(name.length > 200) 159 | { 160 | // there's a bug in SQLite which causes it to sometimes hang on entries with > 200 chars 161 | name = [name substringToIndex:200]; 162 | } 163 | NSString *parsedPath = path; 164 | if([parsedPath rangeOfString:@"#"].location != NSNotFound) 165 | { 166 | parsedPath = [parsedPath substringToIndex:[parsedPath rangeOfString:@"#"].location]; 167 | } 168 | // NSString *fullPath = [self.documentsDir stringByAppendingPathComponent:parsedPath]; 169 | // BOOL isDir = NO; 170 | // if(![[NSFileManager defaultManager] fileExistsAtPath:fullPath isDirectory:&isDir]) 171 | // { 172 | // printf("Warning: did not add %s of type %s at path %s. Reason: file does not exist\n", [name UTF8String], [type UTF8String], [path UTF8String]); 173 | // return; 174 | // } 175 | // if(isDir) 176 | // { 177 | // printf("Warning: did not add %s of type %s at path %s. Reason: path is a folder\n", [name UTF8String], [type UTF8String], [path UTF8String]); 178 | // return; 179 | // } 180 | NSString *add = [NSString stringWithFormat:@"%@%@%@", name, type, parsedPath]; 181 | if(![self.added containsObject:add]) 182 | { 183 | [self.added addObject:add]; 184 | [self.db executeUpdate:@"INSERT INTO searchIndex(name, type, path) VALUES (?, ?, ?)", name, type, path]; 185 | } 186 | } 187 | 188 | - (void)initDB 189 | { 190 | self.db = [FMDatabase databaseWithPath:[self dbPath]]; 191 | [self.db open]; 192 | [self.db beginDeferredTransaction]; 193 | [self.db executeUpdate:@"CREATE TABLE searchIndex(id INTEGER PRIMARY KEY, name TEXT, type TEXT, path TEXT)"]; 194 | } 195 | 196 | - (NSString *)dbPath 197 | { 198 | return [self.resourcesDir stringByAppendingPathComponent:@"docSet.dsidx"]; 199 | } 200 | 201 | - (id)init 202 | { 203 | self = [super init]; 204 | if(self) 205 | { 206 | NSArray *arguments = [[NSProcessInfo processInfo] arguments]; 207 | if(arguments.count == 2 && [[arguments objectAtIndex:1] isEqualToString:@"--help"]) 208 | { 209 | [self printUsage]; 210 | exit(0); 211 | } 212 | if(arguments.count != 3) 213 | { 214 | printf("Error: too %s arguments\n", (arguments.count > 3) ? "many" : "few"); 215 | [self printUsage]; 216 | return nil; 217 | } 218 | setbuf(stdout, NULL); 219 | printf("Creating docset structure..."); 220 | NSString *name = [arguments objectAtIndex:1]; 221 | NSString *path = [arguments objectAtIndex:2]; 222 | NSFileManager *fileManager = [NSFileManager defaultManager]; 223 | self.workingDir = [fileManager currentDirectoryPath]; 224 | if(![path hasPrefix:@"/"]) 225 | { 226 | path = [self.workingDir stringByAppendingPathComponent:path]; 227 | } 228 | path = [path stringByStandardizingPath]; 229 | self.apiPath = path; 230 | self.docsetName = name; 231 | self.docsetPath = [self.workingDir stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.docset", name]]; 232 | self.contentsDir = [self.docsetPath stringByAppendingPathComponent:@"Contents"]; 233 | self.resourcesDir = [self.contentsDir stringByAppendingPathComponent:@"Resources"]; 234 | self.documentsDir = [self.resourcesDir stringByAppendingPathComponent:@"Documents"]; 235 | if([fileManager fileExistsAtPath:self.docsetPath]) 236 | { 237 | [fileManager removeItemAtPath:self.docsetPath error:nil]; 238 | } 239 | NSError *error = nil; 240 | if(![fileManager createDirectoryAtPath:self.documentsDir withIntermediateDirectories:YES attributes:nil error:&error]) 241 | { 242 | printf("\nError: could not create docset directory structure \"%s\"\n", [self.documentsDir UTF8String]); 243 | printf("File manager error was %s\n", [[error localizedDescription] UTF8String]); 244 | return nil; 245 | } 246 | NSString *docsetIndexFile = nil; 247 | NSString *summaryPath = [self.apiPath stringByAppendingPathComponent:@"overview-summary.html"]; 248 | BOOL foundSummary = NO; 249 | if(![fileManager fileExistsAtPath:summaryPath]) 250 | { 251 | NSDirectoryEnumerator *dirEnum = [fileManager enumeratorAtPath:self.apiPath]; 252 | NSInteger count = 0; 253 | NSString *file = nil; 254 | while((file = [dirEnum nextObject]) && count < 10000) 255 | { 256 | if([file isEqualToString:@"overview-summary.html"]) 257 | { 258 | self.apiPath = [[self.apiPath stringByAppendingPathComponent:file] stringByDeletingLastPathComponent]; 259 | foundSummary = YES; 260 | } 261 | ++count; 262 | } 263 | } 264 | else 265 | { 266 | foundSummary = YES; 267 | } 268 | if(foundSummary) 269 | { 270 | docsetIndexFile = @"overview-summary.html"; 271 | } 272 | if([fileManager fileExistsAtPath:[self.apiPath stringByAppendingPathComponent:@"index-files"]]) 273 | { 274 | docsetIndexFile = (docsetIndexFile) ? docsetIndexFile : @"index-files/index-1.html"; 275 | self.hasMultipleIndexes = YES; 276 | } 277 | printf("done\n"); 278 | [self copyFiles]; 279 | self.toIndex = [NSMutableArray array]; 280 | if(!self.hasMultipleIndexes && [fileManager fileExistsAtPath:[self.documentsDir stringByAppendingPathComponent:@"index-all.html"]]) 281 | { 282 | [self.toIndex addObject:[self.documentsDir stringByAppendingPathComponent:@"index-all.html"]]; 283 | docsetIndexFile = (docsetIndexFile) ? docsetIndexFile : @"index-all.html"; 284 | } 285 | else 286 | { 287 | NSString *indexFilesPath = [self.documentsDir stringByAppendingPathComponent:@"index-files"]; 288 | NSDirectoryEnumerator *dirEnum = [fileManager enumeratorAtPath:indexFilesPath]; 289 | NSString *indexFile = nil; 290 | while(indexFile = [dirEnum nextObject]) 291 | { 292 | if([indexFile hasPrefix:@"index-"] && [indexFile hasSuffix:@".html"]) 293 | { 294 | [self.toIndex addObject:[indexFilesPath stringByAppendingPathComponent:indexFile]]; 295 | } 296 | } 297 | } 298 | if(!self.toIndex.count) 299 | { 300 | printf("\nError: The API folder you specified does not contain any index files (either a index-all.html file or a index-files folder) and is not valid. Please contact the developer if you receive this error by mistake.\n\n"); 301 | [self printUsage]; 302 | return nil; 303 | } 304 | [self writeInfoPlist:docsetIndexFile]; 305 | [self startIndexing]; 306 | } 307 | return self; 308 | } 309 | 310 | - (void)writeInfoPlist:(NSString *)docsetIndexFile 311 | { 312 | NSString *platform = [[[self.docsetName componentsSeparatedByString:@" "] objectAtIndex:0] lowercaseString]; 313 | [[NSString stringWithFormat:@"CFBundleIdentifier%@CFBundleName%@DocSetPlatformFamily%@dashIndexFilePath%@DashDocSetFamilyjavaisDashDocset", platform, self.docsetName, platform, docsetIndexFile] writeToFile:[self.contentsDir stringByAppendingPathComponent:@"Info.plist"] atomically:NO encoding:NSUTF8StringEncoding error:nil]; 314 | } 315 | 316 | - (void)copyFiles 317 | { 318 | printf("Copying files..."); 319 | NSFileManager *fileManager = [NSFileManager defaultManager]; 320 | NSDirectoryEnumerator *dirEnum = [fileManager enumeratorAtPath:self.apiPath]; 321 | NSString *file = nil; 322 | while(file = [dirEnum nextObject]) 323 | { 324 | BOOL isDir = NO; 325 | NSString *fullPath = [self.apiPath stringByAppendingPathComponent:file]; 326 | if([fileManager fileExistsAtPath:fullPath isDirectory:&isDir]) 327 | { 328 | if(isDir) 329 | { 330 | [dirEnum skipDescendants]; 331 | } 332 | NSError *error = nil; 333 | if(![fileManager copyItemAtPath:fullPath toPath:[self.documentsDir stringByAppendingPathComponent:file] error:&error]) 334 | { 335 | printf("\nCould not copy %s, error message: %s\n", [file UTF8String], [[error localizedDescription] UTF8String]); 336 | } 337 | } 338 | } 339 | printf("done\n"); 340 | } 341 | 342 | - (void)printUsage 343 | { 344 | printf("Usage: javadocset \n - anything you want\n - the path of the javadoc API folder you want to index\n"); 345 | } 346 | 347 | @end 348 | -------------------------------------------------------------------------------- /javadocset/FMDatabase.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "sqlite3.h" 3 | #import "FMResultSet.h" 4 | #import "FMDatabasePool.h" 5 | 6 | 7 | #if ! __has_feature(objc_arc) 8 | #define FMDBAutorelease(__v) ([__v autorelease]); 9 | #define FMDBReturnAutoreleased FMDBAutorelease 10 | 11 | #define FMDBRetain(__v) ([__v retain]); 12 | #define FMDBReturnRetained FMDBRetain 13 | 14 | #define FMDBRelease(__v) ([__v release]); 15 | 16 | #define FMDBDispatchQueueRelease(__v) (dispatch_release(__v)); 17 | #else 18 | // -fobjc-arc 19 | #define FMDBAutorelease(__v) 20 | #define FMDBReturnAutoreleased(__v) (__v) 21 | 22 | #define FMDBRetain(__v) 23 | #define FMDBReturnRetained(__v) (__v) 24 | 25 | #define FMDBRelease(__v) 26 | 27 | #if TARGET_OS_IPHONE 28 | // Compiling for iOS 29 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 60000 30 | // iOS 6.0 or later 31 | #define FMDBDispatchQueueRelease(__v) 32 | #else 33 | // iOS 5.X or earlier 34 | #define FMDBDispatchQueueRelease(__v) (dispatch_release(__v)); 35 | #endif 36 | #else 37 | // Compiling for Mac OS X 38 | #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 39 | // Mac OS X 10.8 or later 40 | #define FMDBDispatchQueueRelease(__v) 41 | #else 42 | // Mac OS X 10.7 or earlier 43 | #define FMDBDispatchQueueRelease(__v) (dispatch_release(__v)); 44 | #endif 45 | #endif 46 | #endif 47 | 48 | 49 | @interface FMDatabase : NSObject { 50 | 51 | sqlite3* _db; 52 | NSString* _databasePath; 53 | BOOL _logsErrors; 54 | BOOL _crashOnErrors; 55 | BOOL _traceExecution; 56 | BOOL _checkedOut; 57 | BOOL _shouldCacheStatements; 58 | BOOL _isExecutingStatement; 59 | BOOL _inTransaction; 60 | int _busyRetryTimeout; 61 | 62 | NSMutableDictionary *_cachedStatements; 63 | NSMutableSet *_openResultSets; 64 | NSMutableSet *_openFunctions; 65 | 66 | } 67 | 68 | 69 | @property (atomic, assign) BOOL traceExecution; 70 | @property (atomic, assign) BOOL checkedOut; 71 | @property (atomic, assign) int busyRetryTimeout; 72 | @property (atomic, assign) BOOL crashOnErrors; 73 | @property (atomic, assign) BOOL logsErrors; 74 | @property (atomic, retain) NSMutableDictionary *cachedStatements; 75 | 76 | 77 | + (id)databaseWithPath:(NSString*)inPath; 78 | - (id)initWithPath:(NSString*)inPath; 79 | 80 | - (BOOL)open; 81 | #if SQLITE_VERSION_NUMBER >= 3005000 82 | - (BOOL)openWithFlags:(int)flags; 83 | #endif 84 | - (BOOL)close; 85 | - (BOOL)goodConnection; 86 | - (void)clearCachedStatements; 87 | - (void)closeOpenResultSets; 88 | - (BOOL)hasOpenResultSets; 89 | 90 | // encryption methods. You need to have purchased the sqlite encryption extensions for these to work. 91 | - (BOOL)setKey:(NSString*)key; 92 | - (BOOL)rekey:(NSString*)key; 93 | - (BOOL)setKeyWithData:(NSData *)keyData; 94 | - (BOOL)rekeyWithData:(NSData *)keyData; 95 | 96 | - (NSString *)databasePath; 97 | 98 | - (NSString*)lastErrorMessage; 99 | 100 | - (int)lastErrorCode; 101 | - (BOOL)hadError; 102 | - (NSError*)lastError; 103 | 104 | - (sqlite_int64)lastInsertRowId; 105 | 106 | - (sqlite3*)sqliteHandle; 107 | 108 | - (BOOL)update:(NSString*)sql withErrorAndBindings:(NSError**)outErr, ...; 109 | - (BOOL)executeUpdate:(NSString*)sql, ...; 110 | - (BOOL)executeUpdateWithFormat:(NSString *)format, ...; 111 | - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments; 112 | - (BOOL)executeUpdate:(NSString*)sql withParameterDictionary:(NSDictionary *)arguments; 113 | 114 | - (FMResultSet *)executeQuery:(NSString*)sql, ...; 115 | - (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...; 116 | - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments; 117 | - (FMResultSet *)executeQuery:(NSString *)sql withParameterDictionary:(NSDictionary *)arguments; 118 | 119 | - (BOOL)rollback; 120 | - (BOOL)commit; 121 | - (BOOL)beginTransaction; 122 | - (BOOL)beginDeferredTransaction; 123 | - (BOOL)inTransaction; 124 | - (BOOL)shouldCacheStatements; 125 | - (void)setShouldCacheStatements:(BOOL)value; 126 | 127 | #if SQLITE_VERSION_NUMBER >= 3007000 128 | - (BOOL)startSavePointWithName:(NSString*)name error:(NSError**)outErr; 129 | - (BOOL)releaseSavePointWithName:(NSString*)name error:(NSError**)outErr; 130 | - (BOOL)rollbackToSavePointWithName:(NSString*)name error:(NSError**)outErr; 131 | - (NSError*)inSavePoint:(void (^)(BOOL *rollback))block; 132 | #endif 133 | 134 | + (BOOL)isSQLiteThreadSafe; 135 | + (NSString*)sqliteLibVersion; 136 | 137 | - (int)changes; 138 | 139 | - (void)makeFunctionNamed:(NSString*)name maximumArguments:(int)count withBlock:(void (^)(sqlite3_context *context, int argc, sqlite3_value **argv))block; 140 | 141 | @end 142 | 143 | @interface FMStatement : NSObject { 144 | sqlite3_stmt *_statement; 145 | NSString *_query; 146 | long _useCount; 147 | } 148 | 149 | @property (atomic, assign) long useCount; 150 | @property (atomic, retain) NSString *query; 151 | @property (atomic, assign) sqlite3_stmt *statement; 152 | 153 | - (void)close; 154 | - (void)reset; 155 | 156 | @end 157 | 158 | -------------------------------------------------------------------------------- /javadocset/FMDatabase.m: -------------------------------------------------------------------------------- 1 | #import "FMDatabase.h" 2 | #import "unistd.h" 3 | #import 4 | 5 | @interface FMDatabase () 6 | 7 | - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args; 8 | - (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args; 9 | @end 10 | 11 | @implementation FMDatabase 12 | @synthesize cachedStatements=_cachedStatements; 13 | @synthesize logsErrors=_logsErrors; 14 | @synthesize crashOnErrors=_crashOnErrors; 15 | @synthesize busyRetryTimeout=_busyRetryTimeout; 16 | @synthesize checkedOut=_checkedOut; 17 | @synthesize traceExecution=_traceExecution; 18 | 19 | + (id)databaseWithPath:(NSString*)aPath { 20 | return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath]); 21 | } 22 | 23 | + (NSString*)sqliteLibVersion { 24 | return [NSString stringWithFormat:@"%s", sqlite3_libversion()]; 25 | } 26 | 27 | + (BOOL)isSQLiteThreadSafe { 28 | // make sure to read the sqlite headers on this guy! 29 | return sqlite3_threadsafe() != 0; 30 | } 31 | 32 | - (id)initWithPath:(NSString*)aPath { 33 | 34 | assert(sqlite3_threadsafe()); // whoa there big boy- gotta make sure sqlite it happy with what we're going to do. 35 | 36 | self = [super init]; 37 | 38 | if (self) { 39 | _databasePath = [aPath copy]; 40 | _openResultSets = [[NSMutableSet alloc] init]; 41 | _db = 0x00; 42 | _logsErrors = 0x00; 43 | _crashOnErrors = 0x00; 44 | _busyRetryTimeout = 0x00; 45 | } 46 | 47 | return self; 48 | } 49 | 50 | - (void)finalize { 51 | [self close]; 52 | [super finalize]; 53 | } 54 | 55 | - (void)dealloc { 56 | [self close]; 57 | FMDBRelease(_openResultSets); 58 | FMDBRelease(_cachedStatements); 59 | FMDBRelease(_databasePath); 60 | FMDBRelease(_openFunctions); 61 | 62 | #if ! __has_feature(objc_arc) 63 | [super dealloc]; 64 | #endif 65 | } 66 | 67 | - (NSString *)databasePath { 68 | return _databasePath; 69 | } 70 | 71 | - (sqlite3*)sqliteHandle { 72 | return _db; 73 | } 74 | 75 | - (const char*)sqlitePath { 76 | 77 | if (!_databasePath) { 78 | return ":memory:"; 79 | } 80 | 81 | if ([_databasePath length] == 0) { 82 | return ""; // this creates a temporary database (it's an sqlite thing). 83 | } 84 | 85 | return [_databasePath fileSystemRepresentation]; 86 | 87 | } 88 | 89 | - (BOOL)open { 90 | if (_db) { 91 | return YES; 92 | } 93 | 94 | int err = sqlite3_open([self sqlitePath], &_db ); 95 | if(err != SQLITE_OK) { 96 | NSLog(@"error opening!: %d", err); 97 | return NO; 98 | } 99 | 100 | return YES; 101 | } 102 | 103 | #if SQLITE_VERSION_NUMBER >= 3005000 104 | - (BOOL)openWithFlags:(int)flags { 105 | int err = sqlite3_open_v2([self sqlitePath], &_db, flags, NULL /* Name of VFS module to use */); 106 | if(err != SQLITE_OK) { 107 | NSLog(@"error opening!: %d", err); 108 | return NO; 109 | } 110 | return YES; 111 | } 112 | #endif 113 | 114 | 115 | - (BOOL)close { 116 | 117 | [self clearCachedStatements]; 118 | [self closeOpenResultSets]; 119 | 120 | if (!_db) { 121 | return YES; 122 | } 123 | 124 | int rc; 125 | BOOL retry; 126 | int numberOfRetries = 0; 127 | BOOL triedFinalizingOpenStatements = NO; 128 | 129 | do { 130 | retry = NO; 131 | rc = sqlite3_close(_db); 132 | 133 | if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) { 134 | 135 | retry = YES; 136 | usleep(20); 137 | 138 | if (_busyRetryTimeout && (numberOfRetries++ > _busyRetryTimeout)) { 139 | NSLog(@"%s:%d", __FUNCTION__, __LINE__); 140 | NSLog(@"Database busy, unable to close"); 141 | return NO; 142 | } 143 | 144 | if (!triedFinalizingOpenStatements) { 145 | triedFinalizingOpenStatements = YES; 146 | sqlite3_stmt *pStmt; 147 | while ((pStmt = sqlite3_next_stmt(_db, 0x00)) !=0) { 148 | NSLog(@"Closing leaked statement"); 149 | sqlite3_finalize(pStmt); 150 | } 151 | } 152 | } 153 | else if (SQLITE_OK != rc) { 154 | NSLog(@"error closing!: %d", rc); 155 | } 156 | } 157 | while (retry); 158 | 159 | _db = nil; 160 | return YES; 161 | } 162 | 163 | - (void)clearCachedStatements { 164 | 165 | for (FMStatement *cachedStmt in [_cachedStatements objectEnumerator]) { 166 | [cachedStmt close]; 167 | } 168 | 169 | [_cachedStatements removeAllObjects]; 170 | } 171 | 172 | - (BOOL)hasOpenResultSets { 173 | return [_openResultSets count] > 0; 174 | } 175 | 176 | - (void)closeOpenResultSets { 177 | 178 | //Copy the set so we don't get mutation errors 179 | NSSet *openSetCopy = FMDBReturnAutoreleased([_openResultSets copy]); 180 | for (NSValue *rsInWrappedInATastyValueMeal in openSetCopy) { 181 | FMResultSet *rs = (FMResultSet *)[rsInWrappedInATastyValueMeal pointerValue]; 182 | 183 | [rs setParentDB:nil]; 184 | [rs close]; 185 | 186 | [_openResultSets removeObject:rsInWrappedInATastyValueMeal]; 187 | } 188 | } 189 | 190 | - (void)resultSetDidClose:(FMResultSet *)resultSet { 191 | NSValue *setValue = [NSValue valueWithNonretainedObject:resultSet]; 192 | 193 | [_openResultSets removeObject:setValue]; 194 | } 195 | 196 | - (FMStatement*)cachedStatementForQuery:(NSString*)query { 197 | return [_cachedStatements objectForKey:query]; 198 | } 199 | 200 | - (void)setCachedStatement:(FMStatement*)statement forQuery:(NSString*)query { 201 | 202 | query = [query copy]; // in case we got handed in a mutable string... 203 | 204 | [statement setQuery:query]; 205 | 206 | [_cachedStatements setObject:statement forKey:query]; 207 | 208 | FMDBRelease(query); 209 | } 210 | 211 | 212 | - (BOOL)rekey:(NSString*)key { 213 | NSData *keyData = [NSData dataWithBytes:(void *)[key UTF8String] length:(int)strlen([key UTF8String])]; 214 | 215 | return [self rekeyWithData:keyData]; 216 | } 217 | 218 | - (BOOL)rekeyWithData:(NSData *)keyData { 219 | #ifdef SQLITE_HAS_CODEC 220 | if (!keyData) { 221 | return NO; 222 | } 223 | 224 | int rc = sqlite3_rekey(_db, [keyData bytes], (int)[keyData length]); 225 | 226 | if (rc != SQLITE_OK) { 227 | NSLog(@"error on rekey: %d", rc); 228 | NSLog(@"%@", [self lastErrorMessage]); 229 | } 230 | 231 | return (rc == SQLITE_OK); 232 | #else 233 | return NO; 234 | #endif 235 | } 236 | 237 | - (BOOL)setKey:(NSString*)key { 238 | NSData *keyData = [NSData dataWithBytes:[key UTF8String] length:(int)strlen([key UTF8String])]; 239 | 240 | return [self setKeyWithData:keyData]; 241 | } 242 | 243 | - (BOOL)setKeyWithData:(NSData *)keyData { 244 | #ifdef SQLITE_HAS_CODEC 245 | if (!keyData) { 246 | return NO; 247 | } 248 | 249 | int rc = sqlite3_key(_db, [keyData bytes], (int)[keyData length]); 250 | 251 | return (rc == SQLITE_OK); 252 | #else 253 | return NO; 254 | #endif 255 | } 256 | 257 | - (BOOL)goodConnection { 258 | 259 | if (!_db) { 260 | return NO; 261 | } 262 | 263 | FMResultSet *rs = [self executeQuery:@"select name from sqlite_master where type='table'"]; 264 | 265 | if (rs) { 266 | [rs close]; 267 | return YES; 268 | } 269 | 270 | return NO; 271 | } 272 | 273 | - (void)warnInUse { 274 | NSLog(@"The FMDatabase %@ is currently in use.", self); 275 | 276 | #ifndef NS_BLOCK_ASSERTIONS 277 | if (_crashOnErrors) { 278 | NSAssert1(false, @"The FMDatabase %@ is currently in use.", self); 279 | abort(); 280 | } 281 | #endif 282 | } 283 | 284 | - (BOOL)databaseExists { 285 | 286 | if (!_db) { 287 | 288 | NSLog(@"The FMDatabase %@ is not open.", self); 289 | 290 | #ifndef NS_BLOCK_ASSERTIONS 291 | if (_crashOnErrors) { 292 | NSAssert1(false, @"The FMDatabase %@ is not open.", self); 293 | abort(); 294 | } 295 | #endif 296 | 297 | return NO; 298 | } 299 | 300 | return YES; 301 | } 302 | 303 | - (NSString*)lastErrorMessage { 304 | return [NSString stringWithUTF8String:sqlite3_errmsg(_db)]; 305 | } 306 | 307 | - (BOOL)hadError { 308 | int lastErrCode = [self lastErrorCode]; 309 | 310 | return (lastErrCode > SQLITE_OK && lastErrCode < SQLITE_ROW); 311 | } 312 | 313 | - (int)lastErrorCode { 314 | return sqlite3_errcode(_db); 315 | } 316 | 317 | 318 | - (NSError*)errorWithMessage:(NSString*)message { 319 | NSDictionary* errorMessage = [NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey]; 320 | 321 | return [NSError errorWithDomain:@"FMDatabase" code:sqlite3_errcode(_db) userInfo:errorMessage]; 322 | } 323 | 324 | - (NSError*)lastError { 325 | return [self errorWithMessage:[self lastErrorMessage]]; 326 | } 327 | 328 | - (sqlite_int64)lastInsertRowId { 329 | 330 | if (_isExecutingStatement) { 331 | [self warnInUse]; 332 | return NO; 333 | } 334 | 335 | _isExecutingStatement = YES; 336 | 337 | sqlite_int64 ret = sqlite3_last_insert_rowid(_db); 338 | 339 | _isExecutingStatement = NO; 340 | 341 | return ret; 342 | } 343 | 344 | - (int)changes { 345 | if (_isExecutingStatement) { 346 | [self warnInUse]; 347 | return 0; 348 | } 349 | 350 | _isExecutingStatement = YES; 351 | 352 | int ret = sqlite3_changes(_db); 353 | 354 | _isExecutingStatement = NO; 355 | 356 | return ret; 357 | } 358 | 359 | - (void)bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt*)pStmt { 360 | 361 | if ((!obj) || ((NSNull *)obj == [NSNull null])) { 362 | sqlite3_bind_null(pStmt, idx); 363 | } 364 | 365 | // FIXME - someday check the return codes on these binds. 366 | else if ([obj isKindOfClass:[NSData class]]) { 367 | const void *bytes = [obj bytes]; 368 | if (!bytes) { 369 | // it's an empty NSData object, aka [NSData data]. 370 | // Don't pass a NULL pointer, or sqlite will bind a SQL null instead of a blob. 371 | bytes = ""; 372 | } 373 | sqlite3_bind_blob(pStmt, idx, bytes, (int)[obj length], SQLITE_STATIC); 374 | } 375 | else if ([obj isKindOfClass:[NSDate class]]) { 376 | sqlite3_bind_double(pStmt, idx, [obj timeIntervalSince1970]); 377 | } 378 | else if ([obj isKindOfClass:[NSNumber class]]) { 379 | 380 | if (strcmp([obj objCType], @encode(BOOL)) == 0) { 381 | sqlite3_bind_int(pStmt, idx, ([obj boolValue] ? 1 : 0)); 382 | } 383 | else if (strcmp([obj objCType], @encode(int)) == 0) { 384 | sqlite3_bind_int64(pStmt, idx, [obj longValue]); 385 | } 386 | else if (strcmp([obj objCType], @encode(long)) == 0) { 387 | sqlite3_bind_int64(pStmt, idx, [obj longValue]); 388 | } 389 | else if (strcmp([obj objCType], @encode(long long)) == 0) { 390 | sqlite3_bind_int64(pStmt, idx, [obj longLongValue]); 391 | } 392 | else if (strcmp([obj objCType], @encode(unsigned long long)) == 0) { 393 | sqlite3_bind_int64(pStmt, idx, (long long)[obj unsignedLongLongValue]); 394 | } 395 | else if (strcmp([obj objCType], @encode(float)) == 0) { 396 | sqlite3_bind_double(pStmt, idx, [obj floatValue]); 397 | } 398 | else if (strcmp([obj objCType], @encode(double)) == 0) { 399 | sqlite3_bind_double(pStmt, idx, [obj doubleValue]); 400 | } 401 | else { 402 | sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC); 403 | } 404 | } 405 | else { 406 | sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC); 407 | } 408 | } 409 | 410 | - (void)extractSQL:(NSString *)sql argumentsList:(va_list)args intoString:(NSMutableString *)cleanedSQL arguments:(NSMutableArray *)arguments { 411 | 412 | NSUInteger length = [sql length]; 413 | unichar last = '\0'; 414 | for (NSUInteger i = 0; i < length; ++i) { 415 | id arg = nil; 416 | unichar current = [sql characterAtIndex:i]; 417 | unichar add = current; 418 | if (last == '%') { 419 | switch (current) { 420 | case '@': 421 | arg = va_arg(args, id); 422 | break; 423 | case 'c': 424 | // warning: second argument to 'va_arg' is of promotable type 'char'; this va_arg has undefined behavior because arguments will be promoted to 'int' 425 | arg = [NSString stringWithFormat:@"%c", va_arg(args, int)]; 426 | break; 427 | case 's': 428 | arg = [NSString stringWithUTF8String:va_arg(args, char*)]; 429 | break; 430 | case 'd': 431 | case 'D': 432 | case 'i': 433 | arg = [NSNumber numberWithInt:va_arg(args, int)]; 434 | break; 435 | case 'u': 436 | case 'U': 437 | arg = [NSNumber numberWithUnsignedInt:va_arg(args, unsigned int)]; 438 | break; 439 | case 'h': 440 | i++; 441 | if (i < length && [sql characterAtIndex:i] == 'i') { 442 | // warning: second argument to 'va_arg' is of promotable type 'short'; this va_arg has undefined behavior because arguments will be promoted to 'int' 443 | arg = [NSNumber numberWithShort:(short)(va_arg(args, int))]; 444 | } 445 | else if (i < length && [sql characterAtIndex:i] == 'u') { 446 | // warning: second argument to 'va_arg' is of promotable type 'unsigned short'; this va_arg has undefined behavior because arguments will be promoted to 'int' 447 | arg = [NSNumber numberWithUnsignedShort:(unsigned short)(va_arg(args, uint))]; 448 | } 449 | else { 450 | i--; 451 | } 452 | break; 453 | case 'q': 454 | i++; 455 | if (i < length && [sql characterAtIndex:i] == 'i') { 456 | arg = [NSNumber numberWithLongLong:va_arg(args, long long)]; 457 | } 458 | else if (i < length && [sql characterAtIndex:i] == 'u') { 459 | arg = [NSNumber numberWithUnsignedLongLong:va_arg(args, unsigned long long)]; 460 | } 461 | else { 462 | i--; 463 | } 464 | break; 465 | case 'f': 466 | arg = [NSNumber numberWithDouble:va_arg(args, double)]; 467 | break; 468 | case 'g': 469 | // warning: second argument to 'va_arg' is of promotable type 'float'; this va_arg has undefined behavior because arguments will be promoted to 'double' 470 | arg = [NSNumber numberWithFloat:(float)(va_arg(args, double))]; 471 | break; 472 | case 'l': 473 | i++; 474 | if (i < length) { 475 | unichar next = [sql characterAtIndex:i]; 476 | if (next == 'l') { 477 | i++; 478 | if (i < length && [sql characterAtIndex:i] == 'd') { 479 | //%lld 480 | arg = [NSNumber numberWithLongLong:va_arg(args, long long)]; 481 | } 482 | else if (i < length && [sql characterAtIndex:i] == 'u') { 483 | //%llu 484 | arg = [NSNumber numberWithUnsignedLongLong:va_arg(args, unsigned long long)]; 485 | } 486 | else { 487 | i--; 488 | } 489 | } 490 | else if (next == 'd') { 491 | //%ld 492 | arg = [NSNumber numberWithLong:va_arg(args, long)]; 493 | } 494 | else if (next == 'u') { 495 | //%lu 496 | arg = [NSNumber numberWithUnsignedLong:va_arg(args, unsigned long)]; 497 | } 498 | else { 499 | i--; 500 | } 501 | } 502 | else { 503 | i--; 504 | } 505 | break; 506 | default: 507 | // something else that we can't interpret. just pass it on through like normal 508 | break; 509 | } 510 | } 511 | else if (current == '%') { 512 | // percent sign; skip this character 513 | add = '\0'; 514 | } 515 | 516 | if (arg != nil) { 517 | [cleanedSQL appendString:@"?"]; 518 | [arguments addObject:arg]; 519 | } 520 | else if (add != '\0') { 521 | [cleanedSQL appendFormat:@"%C", add]; 522 | } 523 | last = current; 524 | } 525 | } 526 | 527 | - (FMResultSet *)executeQuery:(NSString *)sql withParameterDictionary:(NSDictionary *)arguments { 528 | return [self executeQuery:sql withArgumentsInArray:nil orDictionary:arguments orVAList:nil]; 529 | } 530 | 531 | - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args { 532 | 533 | if (![self databaseExists]) { 534 | return 0x00; 535 | } 536 | 537 | if (_isExecutingStatement) { 538 | [self warnInUse]; 539 | return 0x00; 540 | } 541 | 542 | _isExecutingStatement = YES; 543 | 544 | int rc = 0x00; 545 | sqlite3_stmt *pStmt = 0x00; 546 | FMStatement *statement = 0x00; 547 | FMResultSet *rs = 0x00; 548 | 549 | if (_traceExecution && sql) { 550 | NSLog(@"%@ executeQuery: %@", self, sql); 551 | } 552 | 553 | if (_shouldCacheStatements) { 554 | statement = [self cachedStatementForQuery:sql]; 555 | pStmt = statement ? [statement statement] : 0x00; 556 | [statement reset]; 557 | } 558 | 559 | int numberOfRetries = 0; 560 | BOOL retry = NO; 561 | 562 | if (!pStmt) { 563 | do { 564 | retry = NO; 565 | rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0); 566 | 567 | if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) { 568 | retry = YES; 569 | usleep(20); 570 | 571 | if (_busyRetryTimeout && (numberOfRetries++ > _busyRetryTimeout)) { 572 | NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]); 573 | NSLog(@"Database busy"); 574 | sqlite3_finalize(pStmt); 575 | _isExecutingStatement = NO; 576 | return nil; 577 | } 578 | } 579 | else if (SQLITE_OK != rc) { 580 | 581 | if (_logsErrors) { 582 | NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]); 583 | NSLog(@"DB Query: %@", sql); 584 | NSLog(@"DB Path: %@", _databasePath); 585 | #ifndef NS_BLOCK_ASSERTIONS 586 | if (_crashOnErrors) { 587 | abort(); 588 | NSAssert2(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]); 589 | } 590 | #endif 591 | } 592 | 593 | sqlite3_finalize(pStmt); 594 | _isExecutingStatement = NO; 595 | return nil; 596 | } 597 | } 598 | while (retry); 599 | } 600 | 601 | id obj; 602 | int idx = 0; 603 | int queryCount = sqlite3_bind_parameter_count(pStmt); // pointed out by Dominic Yu (thanks!) 604 | 605 | // If dictionaryArgs is passed in, that means we are using sqlite's named parameter support 606 | if (dictionaryArgs) { 607 | 608 | for (NSString *dictionaryKey in [dictionaryArgs allKeys]) { 609 | 610 | // Prefix the key with a colon. 611 | NSString *parameterName = [[NSString alloc] initWithFormat:@":%@", dictionaryKey]; 612 | 613 | // Get the index for the parameter name. 614 | int namedIdx = sqlite3_bind_parameter_index(pStmt, [parameterName UTF8String]); 615 | 616 | FMDBRelease(parameterName); 617 | 618 | if (namedIdx > 0) { 619 | // Standard binding from here. 620 | [self bindObject:[dictionaryArgs objectForKey:dictionaryKey] toColumn:namedIdx inStatement:pStmt]; 621 | // increment the binding count, so our check below works out 622 | idx++; 623 | } 624 | else { 625 | NSLog(@"Could not find index for %@", dictionaryKey); 626 | } 627 | } 628 | } 629 | else { 630 | 631 | while (idx < queryCount) { 632 | 633 | if (arrayArgs) { 634 | obj = [arrayArgs objectAtIndex:(NSUInteger)idx]; 635 | } 636 | else { 637 | obj = va_arg(args, id); 638 | } 639 | 640 | if (_traceExecution) { 641 | NSLog(@"obj: %@", obj); 642 | } 643 | 644 | idx++; 645 | 646 | [self bindObject:obj toColumn:idx inStatement:pStmt]; 647 | } 648 | } 649 | 650 | if (idx != queryCount) { 651 | NSLog(@"Error: the bind count is not correct for the # of variables (executeQuery)"); 652 | sqlite3_finalize(pStmt); 653 | _isExecutingStatement = NO; 654 | return nil; 655 | } 656 | 657 | FMDBRetain(statement); // to balance the release below 658 | 659 | if (!statement) { 660 | statement = [[FMStatement alloc] init]; 661 | [statement setStatement:pStmt]; 662 | 663 | if (_shouldCacheStatements) { 664 | [self setCachedStatement:statement forQuery:sql]; 665 | } 666 | } 667 | 668 | // the statement gets closed in rs's dealloc or [rs close]; 669 | rs = [FMResultSet resultSetWithStatement:statement usingParentDatabase:self]; 670 | [rs setQuery:sql]; 671 | 672 | NSValue *openResultSet = [NSValue valueWithNonretainedObject:rs]; 673 | [_openResultSets addObject:openResultSet]; 674 | 675 | [statement setUseCount:[statement useCount] + 1]; 676 | 677 | FMDBRelease(statement); 678 | 679 | _isExecutingStatement = NO; 680 | 681 | return rs; 682 | } 683 | 684 | - (FMResultSet *)executeQuery:(NSString*)sql, ... { 685 | va_list args; 686 | va_start(args, sql); 687 | 688 | id result = [self executeQuery:sql withArgumentsInArray:nil orDictionary:nil orVAList:args]; 689 | 690 | va_end(args); 691 | return result; 692 | } 693 | 694 | - (FMResultSet *)executeQueryWithFormat:(NSString*)format, ... { 695 | va_list args; 696 | va_start(args, format); 697 | 698 | NSMutableString *sql = [NSMutableString stringWithCapacity:[format length]]; 699 | NSMutableArray *arguments = [NSMutableArray array]; 700 | [self extractSQL:format argumentsList:args intoString:sql arguments:arguments]; 701 | 702 | va_end(args); 703 | 704 | return [self executeQuery:sql withArgumentsInArray:arguments]; 705 | } 706 | 707 | - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments { 708 | return [self executeQuery:sql withArgumentsInArray:arguments orDictionary:nil orVAList:nil]; 709 | } 710 | 711 | - (BOOL)executeUpdate:(NSString*)sql error:(NSError**)outErr withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args { 712 | 713 | if (![self databaseExists]) { 714 | return NO; 715 | } 716 | 717 | if (_isExecutingStatement) { 718 | [self warnInUse]; 719 | return NO; 720 | } 721 | 722 | _isExecutingStatement = YES; 723 | 724 | int rc = 0x00; 725 | sqlite3_stmt *pStmt = 0x00; 726 | FMStatement *cachedStmt = 0x00; 727 | 728 | if (_traceExecution && sql) { 729 | NSLog(@"%@ executeUpdate: %@", self, sql); 730 | } 731 | 732 | if (_shouldCacheStatements) { 733 | cachedStmt = [self cachedStatementForQuery:sql]; 734 | pStmt = cachedStmt ? [cachedStmt statement] : 0x00; 735 | [cachedStmt reset]; 736 | } 737 | 738 | int numberOfRetries = 0; 739 | BOOL retry = NO; 740 | 741 | if (!pStmt) { 742 | 743 | do { 744 | retry = NO; 745 | rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0); 746 | if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) { 747 | retry = YES; 748 | usleep(20); 749 | 750 | if (_busyRetryTimeout && (numberOfRetries++ > _busyRetryTimeout)) { 751 | NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]); 752 | NSLog(@"Database busy"); 753 | sqlite3_finalize(pStmt); 754 | _isExecutingStatement = NO; 755 | return NO; 756 | } 757 | } 758 | else if (SQLITE_OK != rc) { 759 | 760 | if (_logsErrors) { 761 | NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]); 762 | NSLog(@"DB Query: %@", sql); 763 | NSLog(@"DB Path: %@", _databasePath); 764 | #ifndef NS_BLOCK_ASSERTIONS 765 | if (_crashOnErrors) { 766 | abort(); 767 | NSAssert2(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]); 768 | } 769 | #endif 770 | } 771 | 772 | sqlite3_finalize(pStmt); 773 | 774 | if (outErr) { 775 | *outErr = [self errorWithMessage:[NSString stringWithUTF8String:sqlite3_errmsg(_db)]]; 776 | } 777 | 778 | _isExecutingStatement = NO; 779 | return NO; 780 | } 781 | } 782 | while (retry); 783 | } 784 | 785 | id obj; 786 | int idx = 0; 787 | int queryCount = sqlite3_bind_parameter_count(pStmt); 788 | 789 | // If dictionaryArgs is passed in, that means we are using sqlite's named parameter support 790 | if (dictionaryArgs) { 791 | 792 | for (NSString *dictionaryKey in [dictionaryArgs allKeys]) { 793 | 794 | // Prefix the key with a colon. 795 | NSString *parameterName = [[NSString alloc] initWithFormat:@":%@", dictionaryKey]; 796 | 797 | // Get the index for the parameter name. 798 | int namedIdx = sqlite3_bind_parameter_index(pStmt, [parameterName UTF8String]); 799 | 800 | FMDBRelease(parameterName); 801 | 802 | if (namedIdx > 0) { 803 | // Standard binding from here. 804 | [self bindObject:[dictionaryArgs objectForKey:dictionaryKey] toColumn:namedIdx inStatement:pStmt]; 805 | 806 | // increment the binding count, so our check below works out 807 | idx++; 808 | } 809 | else { 810 | NSLog(@"Could not find index for %@", dictionaryKey); 811 | } 812 | } 813 | } 814 | else { 815 | 816 | while (idx < queryCount) { 817 | 818 | if (arrayArgs) { 819 | obj = [arrayArgs objectAtIndex:(NSUInteger)idx]; 820 | } 821 | else { 822 | obj = va_arg(args, id); 823 | } 824 | 825 | if (_traceExecution) { 826 | NSLog(@"obj: %@", obj); 827 | } 828 | 829 | idx++; 830 | 831 | [self bindObject:obj toColumn:idx inStatement:pStmt]; 832 | } 833 | } 834 | 835 | 836 | if (idx != queryCount) { 837 | NSLog(@"Error: the bind count (%d) is not correct for the # of variables in the query (%d) (%@) (executeUpdate)", idx, queryCount, sql); 838 | sqlite3_finalize(pStmt); 839 | _isExecutingStatement = NO; 840 | return NO; 841 | } 842 | 843 | /* Call sqlite3_step() to run the virtual machine. Since the SQL being 844 | ** executed is not a SELECT statement, we assume no data will be returned. 845 | */ 846 | numberOfRetries = 0; 847 | 848 | do { 849 | rc = sqlite3_step(pStmt); 850 | retry = NO; 851 | 852 | if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) { 853 | // this will happen if the db is locked, like if we are doing an update or insert. 854 | // in that case, retry the step... and maybe wait just 10 milliseconds. 855 | retry = YES; 856 | if (SQLITE_LOCKED == rc) { 857 | rc = sqlite3_reset(pStmt); 858 | if (rc != SQLITE_LOCKED) { 859 | NSLog(@"Unexpected result from sqlite3_reset (%d) eu", rc); 860 | } 861 | } 862 | usleep(20); 863 | 864 | if (_busyRetryTimeout && (numberOfRetries++ > _busyRetryTimeout)) { 865 | NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]); 866 | NSLog(@"Database busy"); 867 | retry = NO; 868 | } 869 | } 870 | else if (SQLITE_DONE == rc) { 871 | // all is well, let's return. 872 | } 873 | else if (SQLITE_ERROR == rc) { 874 | NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_ERROR", rc, sqlite3_errmsg(_db)); 875 | NSLog(@"DB Query: %@", sql); 876 | } 877 | else if (SQLITE_MISUSE == rc) { 878 | // uh oh. 879 | NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_MISUSE", rc, sqlite3_errmsg(_db)); 880 | NSLog(@"DB Query: %@", sql); 881 | } 882 | else { 883 | // wtf? 884 | NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(_db)); 885 | NSLog(@"DB Query: %@", sql); 886 | } 887 | 888 | } while (retry); 889 | 890 | if (rc == SQLITE_ROW) { 891 | NSAssert1(NO, @"A executeUpdate is being called with a query string '%@'", sql); 892 | } 893 | 894 | if (_shouldCacheStatements && !cachedStmt) { 895 | cachedStmt = [[FMStatement alloc] init]; 896 | 897 | [cachedStmt setStatement:pStmt]; 898 | 899 | [self setCachedStatement:cachedStmt forQuery:sql]; 900 | 901 | FMDBRelease(cachedStmt); 902 | } 903 | 904 | int closeErrorCode; 905 | 906 | if (cachedStmt) { 907 | [cachedStmt setUseCount:[cachedStmt useCount] + 1]; 908 | closeErrorCode = sqlite3_reset(pStmt); 909 | } 910 | else { 911 | /* Finalize the virtual machine. This releases all memory and other 912 | ** resources allocated by the sqlite3_prepare() call above. 913 | */ 914 | closeErrorCode = sqlite3_finalize(pStmt); 915 | } 916 | 917 | if (closeErrorCode != SQLITE_OK) { 918 | NSLog(@"Unknown error finalizing or resetting statement (%d: %s)", closeErrorCode, sqlite3_errmsg(_db)); 919 | NSLog(@"DB Query: %@", sql); 920 | } 921 | 922 | _isExecutingStatement = NO; 923 | return (rc == SQLITE_DONE || rc == SQLITE_OK); 924 | } 925 | 926 | 927 | - (BOOL)executeUpdate:(NSString*)sql, ... { 928 | va_list args; 929 | va_start(args, sql); 930 | 931 | BOOL result = [self executeUpdate:sql error:nil withArgumentsInArray:nil orDictionary:nil orVAList:args]; 932 | 933 | va_end(args); 934 | return result; 935 | } 936 | 937 | - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments { 938 | return [self executeUpdate:sql error:nil withArgumentsInArray:arguments orDictionary:nil orVAList:nil]; 939 | } 940 | 941 | - (BOOL)executeUpdate:(NSString*)sql withParameterDictionary:(NSDictionary *)arguments { 942 | return [self executeUpdate:sql error:nil withArgumentsInArray:nil orDictionary:arguments orVAList:nil]; 943 | } 944 | 945 | - (BOOL)executeUpdateWithFormat:(NSString*)format, ... { 946 | va_list args; 947 | va_start(args, format); 948 | 949 | NSMutableString *sql = [NSMutableString stringWithCapacity:[format length]]; 950 | NSMutableArray *arguments = [NSMutableArray array]; 951 | 952 | [self extractSQL:format argumentsList:args intoString:sql arguments:arguments]; 953 | 954 | va_end(args); 955 | 956 | return [self executeUpdate:sql withArgumentsInArray:arguments]; 957 | } 958 | 959 | - (BOOL)update:(NSString*)sql withErrorAndBindings:(NSError**)outErr, ... { 960 | va_list args; 961 | va_start(args, outErr); 962 | 963 | BOOL result = [self executeUpdate:sql error:outErr withArgumentsInArray:nil orDictionary:nil orVAList:args]; 964 | 965 | va_end(args); 966 | return result; 967 | } 968 | 969 | - (BOOL)rollback { 970 | BOOL b = [self executeUpdate:@"rollback transaction"]; 971 | 972 | if (b) { 973 | _inTransaction = NO; 974 | } 975 | 976 | return b; 977 | } 978 | 979 | - (BOOL)commit { 980 | BOOL b = [self executeUpdate:@"commit transaction"]; 981 | 982 | if (b) { 983 | _inTransaction = NO; 984 | } 985 | 986 | return b; 987 | } 988 | 989 | - (BOOL)beginDeferredTransaction { 990 | 991 | BOOL b = [self executeUpdate:@"begin deferred transaction"]; 992 | if (b) { 993 | _inTransaction = YES; 994 | } 995 | 996 | return b; 997 | } 998 | 999 | - (BOOL)beginTransaction { 1000 | 1001 | BOOL b = [self executeUpdate:@"begin exclusive transaction"]; 1002 | if (b) { 1003 | _inTransaction = YES; 1004 | } 1005 | 1006 | return b; 1007 | } 1008 | 1009 | - (BOOL)inTransaction { 1010 | return _inTransaction; 1011 | } 1012 | 1013 | #if SQLITE_VERSION_NUMBER >= 3007000 1014 | 1015 | - (BOOL)startSavePointWithName:(NSString*)name error:(NSError**)outErr { 1016 | 1017 | // FIXME: make sure the savepoint name doesn't have a ' in it. 1018 | 1019 | NSParameterAssert(name); 1020 | 1021 | if (![self executeUpdate:[NSString stringWithFormat:@"savepoint '%@';", name]]) { 1022 | 1023 | if (outErr) { 1024 | *outErr = [self lastError]; 1025 | } 1026 | 1027 | return NO; 1028 | } 1029 | 1030 | return YES; 1031 | } 1032 | 1033 | - (BOOL)releaseSavePointWithName:(NSString*)name error:(NSError**)outErr { 1034 | 1035 | NSParameterAssert(name); 1036 | 1037 | BOOL worked = [self executeUpdate:[NSString stringWithFormat:@"release savepoint '%@';", name]]; 1038 | 1039 | if (!worked && outErr) { 1040 | *outErr = [self lastError]; 1041 | } 1042 | 1043 | return worked; 1044 | } 1045 | 1046 | - (BOOL)rollbackToSavePointWithName:(NSString*)name error:(NSError**)outErr { 1047 | 1048 | NSParameterAssert(name); 1049 | 1050 | BOOL worked = [self executeUpdate:[NSString stringWithFormat:@"rollback transaction to savepoint '%@';", name]]; 1051 | 1052 | if (!worked && *outErr) { 1053 | *outErr = [self lastError]; 1054 | } 1055 | 1056 | return worked; 1057 | } 1058 | 1059 | - (NSError*)inSavePoint:(void (^)(BOOL *rollback))block { 1060 | static unsigned long savePointIdx = 0; 1061 | 1062 | NSString *name = [NSString stringWithFormat:@"dbSavePoint%ld", savePointIdx++]; 1063 | 1064 | BOOL shouldRollback = NO; 1065 | 1066 | NSError *err = 0x00; 1067 | 1068 | if (![self startSavePointWithName:name error:&err]) { 1069 | return err; 1070 | } 1071 | 1072 | block(&shouldRollback); 1073 | 1074 | if (shouldRollback) { 1075 | [self rollbackToSavePointWithName:name error:&err]; 1076 | } 1077 | else { 1078 | [self releaseSavePointWithName:name error:&err]; 1079 | } 1080 | 1081 | return err; 1082 | } 1083 | 1084 | #endif 1085 | 1086 | 1087 | - (BOOL)shouldCacheStatements { 1088 | return _shouldCacheStatements; 1089 | } 1090 | 1091 | - (void)setShouldCacheStatements:(BOOL)value { 1092 | 1093 | _shouldCacheStatements = value; 1094 | 1095 | if (_shouldCacheStatements && !_cachedStatements) { 1096 | [self setCachedStatements:[NSMutableDictionary dictionary]]; 1097 | } 1098 | 1099 | if (!_shouldCacheStatements) { 1100 | [self setCachedStatements:nil]; 1101 | } 1102 | } 1103 | 1104 | void FMDBBlockSQLiteCallBackFunction(sqlite3_context *context, int argc, sqlite3_value **argv); 1105 | void FMDBBlockSQLiteCallBackFunction(sqlite3_context *context, int argc, sqlite3_value **argv) { 1106 | #if ! __has_feature(objc_arc) 1107 | void (^block)(sqlite3_context *context, int argc, sqlite3_value **argv) = (id)sqlite3_user_data(context); 1108 | #else 1109 | void (^block)(sqlite3_context *context, int argc, sqlite3_value **argv) = (__bridge id)sqlite3_user_data(context); 1110 | #endif 1111 | block(context, argc, argv); 1112 | } 1113 | 1114 | 1115 | - (void)makeFunctionNamed:(NSString*)name maximumArguments:(int)count withBlock:(void (^)(sqlite3_context *context, int argc, sqlite3_value **argv))block { 1116 | 1117 | if (!_openFunctions) { 1118 | _openFunctions = [NSMutableSet new]; 1119 | } 1120 | 1121 | id b = FMDBReturnAutoreleased([block copy]); 1122 | 1123 | [_openFunctions addObject:b]; 1124 | 1125 | /* I tried adding custom functions to release the block when the connection is destroyed- but they seemed to never be called, so we use _openFunctions to store the values instead. */ 1126 | #if ! __has_feature(objc_arc) 1127 | sqlite3_create_function([self sqliteHandle], [name UTF8String], count, SQLITE_UTF8, (void*)b, &FMDBBlockSQLiteCallBackFunction, 0x00, 0x00); 1128 | #else 1129 | sqlite3_create_function([self sqliteHandle], [name UTF8String], count, SQLITE_UTF8, (__bridge void*)b, &FMDBBlockSQLiteCallBackFunction, 0x00, 0x00); 1130 | #endif 1131 | } 1132 | 1133 | @end 1134 | 1135 | 1136 | 1137 | @implementation FMStatement 1138 | @synthesize statement=_statement; 1139 | @synthesize query=_query; 1140 | @synthesize useCount=_useCount; 1141 | 1142 | - (void)finalize { 1143 | [self close]; 1144 | [super finalize]; 1145 | } 1146 | 1147 | - (void)dealloc { 1148 | [self close]; 1149 | FMDBRelease(_query); 1150 | #if ! __has_feature(objc_arc) 1151 | [super dealloc]; 1152 | #endif 1153 | } 1154 | 1155 | - (void)close { 1156 | if (_statement) { 1157 | sqlite3_finalize(_statement); 1158 | _statement = 0x00; 1159 | } 1160 | } 1161 | 1162 | - (void)reset { 1163 | if (_statement) { 1164 | sqlite3_reset(_statement); 1165 | } 1166 | } 1167 | 1168 | - (NSString*)description { 1169 | return [NSString stringWithFormat:@"%@ %ld hit(s) for query %@", [super description], _useCount, _query]; 1170 | } 1171 | 1172 | 1173 | @end 1174 | 1175 | -------------------------------------------------------------------------------- /javadocset/FMDatabaseAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabaseAdditions.h 3 | // fmkit 4 | // 5 | // Created by August Mueller on 10/30/05. 6 | // Copyright 2005 Flying Meat Inc.. All rights reserved. 7 | // 8 | 9 | #import 10 | @interface FMDatabase (FMDatabaseAdditions) 11 | 12 | 13 | - (int)intForQuery:(NSString*)objs, ...; 14 | - (long)longForQuery:(NSString*)objs, ...; 15 | - (BOOL)boolForQuery:(NSString*)objs, ...; 16 | - (double)doubleForQuery:(NSString*)objs, ...; 17 | - (NSString*)stringForQuery:(NSString*)objs, ...; 18 | - (NSData*)dataForQuery:(NSString*)objs, ...; 19 | - (NSDate*)dateForQuery:(NSString*)objs, ...; 20 | 21 | // Notice that there's no dataNoCopyForQuery:. 22 | // That would be a bad idea, because we close out the result set, and then what 23 | // happens to the data that we just didn't copy? Who knows, not I. 24 | 25 | 26 | - (BOOL)tableExists:(NSString*)tableName; 27 | - (FMResultSet*)getSchema; 28 | - (FMResultSet*)getTableSchema:(NSString*)tableName; 29 | 30 | - (BOOL)columnExists:(NSString*)columnName inTableWithName:(NSString*)tableName; 31 | 32 | - (BOOL)validateSQL:(NSString*)sql error:(NSError**)error; 33 | 34 | // deprecated - use columnExists:inTableWithName: instead. 35 | - (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName __attribute__ ((deprecated)); 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /javadocset/FMDatabaseAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabaseAdditions.m 3 | // fmkit 4 | // 5 | // Created by August Mueller on 10/30/05. 6 | // Copyright 2005 Flying Meat Inc.. All rights reserved. 7 | // 8 | 9 | #import "FMDatabase.h" 10 | #import "FMDatabaseAdditions.h" 11 | 12 | @interface FMDatabase (PrivateStuff) 13 | - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args; 14 | @end 15 | 16 | @implementation FMDatabase (FMDatabaseAdditions) 17 | 18 | #define RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(type, sel) \ 19 | va_list args; \ 20 | va_start(args, query); \ 21 | FMResultSet *resultSet = [self executeQuery:query withArgumentsInArray:0x00 orDictionary:0x00 orVAList:args]; \ 22 | va_end(args); \ 23 | if (![resultSet next]) { return (type)0; } \ 24 | type ret = [resultSet sel:0]; \ 25 | [resultSet close]; \ 26 | [resultSet setParentDB:nil]; \ 27 | return ret; 28 | 29 | 30 | - (NSString*)stringForQuery:(NSString*)query, ... { 31 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSString *, stringForColumnIndex); 32 | } 33 | 34 | - (int)intForQuery:(NSString*)query, ... { 35 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(int, intForColumnIndex); 36 | } 37 | 38 | - (long)longForQuery:(NSString*)query, ... { 39 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(long, longForColumnIndex); 40 | } 41 | 42 | - (BOOL)boolForQuery:(NSString*)query, ... { 43 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(BOOL, boolForColumnIndex); 44 | } 45 | 46 | - (double)doubleForQuery:(NSString*)query, ... { 47 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(double, doubleForColumnIndex); 48 | } 49 | 50 | - (NSData*)dataForQuery:(NSString*)query, ... { 51 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSData *, dataForColumnIndex); 52 | } 53 | 54 | - (NSDate*)dateForQuery:(NSString*)query, ... { 55 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSDate *, dateForColumnIndex); 56 | } 57 | 58 | 59 | - (BOOL)tableExists:(NSString*)tableName { 60 | 61 | tableName = [tableName lowercaseString]; 62 | 63 | FMResultSet *rs = [self executeQuery:@"select [sql] from sqlite_master where [type] = 'table' and lower(name) = ?", tableName]; 64 | 65 | //if at least one next exists, table exists 66 | BOOL returnBool = [rs next]; 67 | 68 | //close and free object 69 | [rs close]; 70 | 71 | return returnBool; 72 | } 73 | 74 | /* 75 | get table with list of tables: result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING] 76 | check if table exist in database (patch from OZLB) 77 | */ 78 | - (FMResultSet*)getSchema { 79 | 80 | //result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING] 81 | FMResultSet *rs = [self executeQuery:@"SELECT type, name, tbl_name, rootpage, sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type != 'meta' AND name NOT LIKE 'sqlite_%' ORDER BY tbl_name, type DESC, name"]; 82 | 83 | return rs; 84 | } 85 | 86 | /* 87 | get table schema: result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER] 88 | */ 89 | - (FMResultSet*)getTableSchema:(NSString*)tableName { 90 | 91 | //result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER] 92 | FMResultSet *rs = [self executeQuery:[NSString stringWithFormat: @"PRAGMA table_info('%@')", tableName]]; 93 | 94 | return rs; 95 | } 96 | 97 | - (BOOL)columnExists:(NSString*)columnName inTableWithName:(NSString*)tableName { 98 | 99 | BOOL returnBool = NO; 100 | 101 | tableName = [tableName lowercaseString]; 102 | columnName = [columnName lowercaseString]; 103 | 104 | FMResultSet *rs = [self getTableSchema:tableName]; 105 | 106 | //check if column is present in table schema 107 | while ([rs next]) { 108 | if ([[[rs stringForColumn:@"name"] lowercaseString] isEqualToString:columnName]) { 109 | returnBool = YES; 110 | break; 111 | } 112 | } 113 | 114 | //If this is not done FMDatabase instance stays out of pool 115 | [rs close]; 116 | 117 | return returnBool; 118 | } 119 | 120 | #pragma clang diagnostic push 121 | #pragma clang diagnostic ignored "-Wdeprecated-implementations" 122 | 123 | - (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName __attribute__ ((deprecated)) { 124 | return [self columnExists:columnName inTableWithName:tableName]; 125 | } 126 | 127 | #pragma clang diagnostic pop 128 | 129 | - (BOOL)validateSQL:(NSString*)sql error:(NSError**)error { 130 | sqlite3_stmt *pStmt = NULL; 131 | BOOL validationSucceeded = YES; 132 | BOOL keepTrying = YES; 133 | int numberOfRetries = 0; 134 | 135 | while (keepTrying == YES) { 136 | keepTrying = NO; 137 | int rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0); 138 | if (rc == SQLITE_BUSY || rc == SQLITE_LOCKED) { 139 | keepTrying = YES; 140 | usleep(20); 141 | 142 | if (_busyRetryTimeout && (numberOfRetries++ > _busyRetryTimeout)) { 143 | NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]); 144 | NSLog(@"Database busy"); 145 | } 146 | } 147 | else if (rc != SQLITE_OK) { 148 | validationSucceeded = NO; 149 | if (error) { 150 | *error = [NSError errorWithDomain:NSCocoaErrorDomain 151 | code:[self lastErrorCode] 152 | userInfo:[NSDictionary dictionaryWithObject:[self lastErrorMessage] 153 | forKey:NSLocalizedDescriptionKey]]; 154 | } 155 | } 156 | } 157 | 158 | sqlite3_finalize(pStmt); 159 | 160 | return validationSucceeded; 161 | } 162 | 163 | @end 164 | -------------------------------------------------------------------------------- /javadocset/FMDatabasePool.h: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabasePool.h 3 | // fmdb 4 | // 5 | // Created by August Mueller on 6/22/11. 6 | // Copyright 2011 Flying Meat Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "sqlite3.h" 11 | 12 | /* 13 | 14 | ***README OR SUFFER*** 15 | Before using FMDatabasePool, please consider using FMDatabaseQueue instead. 16 | 17 | If you really really really know what you're doing and FMDatabasePool is what 18 | you really really need (ie, you're using a read only database), OK you can use 19 | it. But just be careful not to deadlock! 20 | 21 | For an example on deadlocking, search for: 22 | ONLY_USE_THE_POOL_IF_YOU_ARE_DOING_READS_OTHERWISE_YOULL_DEADLOCK_USE_FMDATABASEQUEUE_INSTEAD 23 | in the main.m file. 24 | 25 | */ 26 | 27 | 28 | 29 | @class FMDatabase; 30 | 31 | @interface FMDatabasePool : NSObject { 32 | NSString *_path; 33 | 34 | dispatch_queue_t _lockQueue; 35 | 36 | NSMutableArray *_databaseInPool; 37 | NSMutableArray *_databaseOutPool; 38 | 39 | __unsafe_unretained id _delegate; 40 | 41 | NSUInteger _maximumNumberOfDatabasesToCreate; 42 | } 43 | 44 | @property (atomic, retain) NSString *path; 45 | @property (atomic, assign) id delegate; 46 | @property (atomic, assign) NSUInteger maximumNumberOfDatabasesToCreate; 47 | 48 | + (id)databasePoolWithPath:(NSString*)aPath; 49 | - (id)initWithPath:(NSString*)aPath; 50 | 51 | - (NSUInteger)countOfCheckedInDatabases; 52 | - (NSUInteger)countOfCheckedOutDatabases; 53 | - (NSUInteger)countOfOpenDatabases; 54 | - (void)releaseAllDatabases; 55 | 56 | - (void)inDatabase:(void (^)(FMDatabase *db))block; 57 | 58 | - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; 59 | - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; 60 | 61 | #if SQLITE_VERSION_NUMBER >= 3007000 62 | // NOTE: you can not nest these, since calling it will pull another database out of the pool and you'll get a deadlock. 63 | // If you need to nest, use FMDatabase's startSavePointWithName:error: instead. 64 | - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block; 65 | #endif 66 | 67 | @end 68 | 69 | 70 | @interface NSObject (FMDatabasePoolDelegate) 71 | 72 | - (BOOL)databasePool:(FMDatabasePool*)pool shouldAddDatabaseToPool:(FMDatabase*)database; 73 | 74 | @end 75 | 76 | -------------------------------------------------------------------------------- /javadocset/FMDatabasePool.m: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabasePool.m 3 | // fmdb 4 | // 5 | // Created by August Mueller on 6/22/11. 6 | // Copyright 2011 Flying Meat Inc. All rights reserved. 7 | // 8 | 9 | #import "FMDatabasePool.h" 10 | #import "FMDatabase.h" 11 | 12 | @interface FMDatabasePool() 13 | 14 | - (void)pushDatabaseBackInPool:(FMDatabase*)db; 15 | - (FMDatabase*)db; 16 | 17 | @end 18 | 19 | 20 | @implementation FMDatabasePool 21 | @synthesize path=_path; 22 | @synthesize delegate=_delegate; 23 | @synthesize maximumNumberOfDatabasesToCreate=_maximumNumberOfDatabasesToCreate; 24 | 25 | 26 | + (id)databasePoolWithPath:(NSString*)aPath { 27 | return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath]); 28 | } 29 | 30 | - (id)initWithPath:(NSString*)aPath { 31 | 32 | self = [super init]; 33 | 34 | if (self != nil) { 35 | _path = [aPath copy]; 36 | _lockQueue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL); 37 | _databaseInPool = FMDBReturnRetained([NSMutableArray array]); 38 | _databaseOutPool = FMDBReturnRetained([NSMutableArray array]); 39 | } 40 | 41 | return self; 42 | } 43 | 44 | - (void)dealloc { 45 | 46 | _delegate = 0x00; 47 | FMDBRelease(_path); 48 | FMDBRelease(_databaseInPool); 49 | FMDBRelease(_databaseOutPool); 50 | 51 | if (_lockQueue) { 52 | FMDBDispatchQueueRelease(_lockQueue); 53 | _lockQueue = 0x00; 54 | } 55 | #if ! __has_feature(objc_arc) 56 | [super dealloc]; 57 | #endif 58 | } 59 | 60 | 61 | - (void)executeLocked:(void (^)(void))aBlock { 62 | dispatch_sync(_lockQueue, aBlock); 63 | } 64 | 65 | - (void)pushDatabaseBackInPool:(FMDatabase*)db { 66 | 67 | if (!db) { // db can be null if we set an upper bound on the # of databases to create. 68 | return; 69 | } 70 | 71 | [self executeLocked:^() { 72 | 73 | if ([_databaseInPool containsObject:db]) { 74 | [[NSException exceptionWithName:@"Database already in pool" reason:@"The FMDatabase being put back into the pool is already present in the pool" userInfo:nil] raise]; 75 | } 76 | 77 | [_databaseInPool addObject:db]; 78 | [_databaseOutPool removeObject:db]; 79 | 80 | }]; 81 | } 82 | 83 | - (FMDatabase*)db { 84 | 85 | __block FMDatabase *db; 86 | 87 | [self executeLocked:^() { 88 | db = [_databaseInPool lastObject]; 89 | 90 | if (db) { 91 | [_databaseOutPool addObject:db]; 92 | [_databaseInPool removeLastObject]; 93 | } 94 | else { 95 | 96 | if (_maximumNumberOfDatabasesToCreate) { 97 | NSUInteger currentCount = [_databaseOutPool count] + [_databaseInPool count]; 98 | 99 | if (currentCount >= _maximumNumberOfDatabasesToCreate) { 100 | NSLog(@"Maximum number of databases (%ld) has already been reached!", (long)currentCount); 101 | return; 102 | } 103 | } 104 | 105 | db = [FMDatabase databaseWithPath:_path]; 106 | } 107 | 108 | //This ensures that the db is opened before returning 109 | if ([db open]) { 110 | if ([_delegate respondsToSelector:@selector(databasePool:shouldAddDatabaseToPool:)] && ![_delegate databasePool:self shouldAddDatabaseToPool:db]) { 111 | [db close]; 112 | db = 0x00; 113 | } 114 | else { 115 | //It should not get added in the pool twice if lastObject was found 116 | if (![_databaseOutPool containsObject:db]) { 117 | [_databaseOutPool addObject:db]; 118 | } 119 | } 120 | } 121 | else { 122 | NSLog(@"Could not open up the database at path %@", _path); 123 | db = 0x00; 124 | } 125 | }]; 126 | 127 | return db; 128 | } 129 | 130 | - (NSUInteger)countOfCheckedInDatabases { 131 | 132 | __block NSUInteger count; 133 | 134 | [self executeLocked:^() { 135 | count = [_databaseInPool count]; 136 | }]; 137 | 138 | return count; 139 | } 140 | 141 | - (NSUInteger)countOfCheckedOutDatabases { 142 | 143 | __block NSUInteger count; 144 | 145 | [self executeLocked:^() { 146 | count = [_databaseOutPool count]; 147 | }]; 148 | 149 | return count; 150 | } 151 | 152 | - (NSUInteger)countOfOpenDatabases { 153 | __block NSUInteger count; 154 | 155 | [self executeLocked:^() { 156 | count = [_databaseOutPool count] + [_databaseInPool count]; 157 | }]; 158 | 159 | return count; 160 | } 161 | 162 | - (void)releaseAllDatabases { 163 | [self executeLocked:^() { 164 | [_databaseOutPool removeAllObjects]; 165 | [_databaseInPool removeAllObjects]; 166 | }]; 167 | } 168 | 169 | - (void)inDatabase:(void (^)(FMDatabase *db))block { 170 | 171 | FMDatabase *db = [self db]; 172 | 173 | block(db); 174 | 175 | [self pushDatabaseBackInPool:db]; 176 | } 177 | 178 | - (void)beginTransaction:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block { 179 | 180 | BOOL shouldRollback = NO; 181 | 182 | FMDatabase *db = [self db]; 183 | 184 | if (useDeferred) { 185 | [db beginDeferredTransaction]; 186 | } 187 | else { 188 | [db beginTransaction]; 189 | } 190 | 191 | 192 | block(db, &shouldRollback); 193 | 194 | if (shouldRollback) { 195 | [db rollback]; 196 | } 197 | else { 198 | [db commit]; 199 | } 200 | 201 | [self pushDatabaseBackInPool:db]; 202 | } 203 | 204 | - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { 205 | [self beginTransaction:YES withBlock:block]; 206 | } 207 | 208 | - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { 209 | [self beginTransaction:NO withBlock:block]; 210 | } 211 | #if SQLITE_VERSION_NUMBER >= 3007000 212 | - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block { 213 | 214 | static unsigned long savePointIdx = 0; 215 | 216 | NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++]; 217 | 218 | BOOL shouldRollback = NO; 219 | 220 | FMDatabase *db = [self db]; 221 | 222 | NSError *err = 0x00; 223 | 224 | if (![db startSavePointWithName:name error:&err]) { 225 | [self pushDatabaseBackInPool:db]; 226 | return err; 227 | } 228 | 229 | block(db, &shouldRollback); 230 | 231 | if (shouldRollback) { 232 | [db rollbackToSavePointWithName:name error:&err]; 233 | } 234 | else { 235 | [db releaseSavePointWithName:name error:&err]; 236 | } 237 | 238 | [self pushDatabaseBackInPool:db]; 239 | 240 | return err; 241 | } 242 | #endif 243 | 244 | @end 245 | -------------------------------------------------------------------------------- /javadocset/FMDatabaseQueue.h: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabaseQueue.h 3 | // fmdb 4 | // 5 | // Created by August Mueller on 6/22/11. 6 | // Copyright 2011 Flying Meat Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "sqlite3.h" 11 | 12 | @class FMDatabase; 13 | 14 | @interface FMDatabaseQueue : NSObject { 15 | NSString *_path; 16 | dispatch_queue_t _queue; 17 | FMDatabase *_db; 18 | } 19 | 20 | @property (atomic, retain) NSString *path; 21 | 22 | + (id)databaseQueueWithPath:(NSString*)aPath; 23 | - (id)initWithPath:(NSString*)aPath; 24 | - (void)close; 25 | 26 | - (void)inDatabase:(void (^)(FMDatabase *db))block; 27 | 28 | - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; 29 | - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; 30 | 31 | #if SQLITE_VERSION_NUMBER >= 3007000 32 | // NOTE: you can not nest these, since calling it will pull another database out of the pool and you'll get a deadlock. 33 | // If you need to nest, use FMDatabase's startSavePointWithName:error: instead. 34 | - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block; 35 | #endif 36 | 37 | @end 38 | 39 | -------------------------------------------------------------------------------- /javadocset/FMDatabaseQueue.m: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabaseQueue.m 3 | // fmdb 4 | // 5 | // Created by August Mueller on 6/22/11. 6 | // Copyright 2011 Flying Meat Inc. All rights reserved. 7 | // 8 | 9 | #import "FMDatabaseQueue.h" 10 | #import "FMDatabase.h" 11 | 12 | /* 13 | 14 | Note: we call [self retain]; before using dispatch_sync, just incase 15 | FMDatabaseQueue is released on another thread and we're in the middle of doing 16 | something in dispatch_sync 17 | 18 | */ 19 | 20 | @implementation FMDatabaseQueue 21 | 22 | @synthesize path = _path; 23 | 24 | + (id)databaseQueueWithPath:(NSString*)aPath { 25 | 26 | FMDatabaseQueue *q = [[self alloc] initWithPath:aPath]; 27 | 28 | FMDBAutorelease(q); 29 | 30 | return q; 31 | } 32 | 33 | - (id)initWithPath:(NSString*)aPath { 34 | 35 | self = [super init]; 36 | 37 | if (self != nil) { 38 | 39 | _db = [FMDatabase databaseWithPath:aPath]; 40 | FMDBRetain(_db); 41 | 42 | if (![_db open]) { 43 | NSLog(@"Could not create database queue for path %@", aPath); 44 | FMDBRelease(self); 45 | return 0x00; 46 | } 47 | 48 | _path = FMDBReturnRetained(aPath); 49 | 50 | _queue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL); 51 | } 52 | 53 | return self; 54 | } 55 | 56 | - (void)dealloc { 57 | 58 | FMDBRelease(_db); 59 | FMDBRelease(_path); 60 | 61 | if (_queue) { 62 | FMDBDispatchQueueRelease(_queue); 63 | _queue = 0x00; 64 | } 65 | #if ! __has_feature(objc_arc) 66 | [super dealloc]; 67 | #endif 68 | } 69 | 70 | - (void)close { 71 | FMDBRetain(self); 72 | dispatch_sync(_queue, ^() { 73 | [_db close]; 74 | FMDBRelease(_db); 75 | _db = 0x00; 76 | }); 77 | FMDBRelease(self); 78 | } 79 | 80 | - (FMDatabase*)database { 81 | if (!_db) { 82 | _db = FMDBReturnRetained([FMDatabase databaseWithPath:_path]); 83 | 84 | if (![_db open]) { 85 | NSLog(@"FMDatabaseQueue could not reopen database for path %@", _path); 86 | FMDBRelease(_db); 87 | _db = 0x00; 88 | return 0x00; 89 | } 90 | } 91 | 92 | return _db; 93 | } 94 | 95 | - (void)inDatabase:(void (^)(FMDatabase *db))block { 96 | FMDBRetain(self); 97 | 98 | dispatch_sync(_queue, ^() { 99 | 100 | FMDatabase *db = [self database]; 101 | block(db); 102 | 103 | if ([db hasOpenResultSets]) { 104 | NSLog(@"Warning: there is at least one open result set around after performing [FMDatabaseQueue inDatabase:]"); 105 | } 106 | }); 107 | 108 | FMDBRelease(self); 109 | } 110 | 111 | 112 | - (void)beginTransaction:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block { 113 | FMDBRetain(self); 114 | dispatch_sync(_queue, ^() { 115 | 116 | BOOL shouldRollback = NO; 117 | 118 | if (useDeferred) { 119 | [[self database] beginDeferredTransaction]; 120 | } 121 | else { 122 | [[self database] beginTransaction]; 123 | } 124 | 125 | block([self database], &shouldRollback); 126 | 127 | if (shouldRollback) { 128 | [[self database] rollback]; 129 | } 130 | else { 131 | [[self database] commit]; 132 | } 133 | }); 134 | 135 | FMDBRelease(self); 136 | } 137 | 138 | - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { 139 | [self beginTransaction:YES withBlock:block]; 140 | } 141 | 142 | - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { 143 | [self beginTransaction:NO withBlock:block]; 144 | } 145 | 146 | #if SQLITE_VERSION_NUMBER >= 3007000 147 | - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block { 148 | 149 | static unsigned long savePointIdx = 0; 150 | __block NSError *err = 0x00; 151 | FMDBRetain(self); 152 | dispatch_sync(_queue, ^() { 153 | 154 | NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++]; 155 | 156 | BOOL shouldRollback = NO; 157 | 158 | if ([[self database] startSavePointWithName:name error:&err]) { 159 | 160 | block([self database], &shouldRollback); 161 | 162 | if (shouldRollback) { 163 | [[self database] rollbackToSavePointWithName:name error:&err]; 164 | } 165 | else { 166 | [[self database] releaseSavePointWithName:name error:&err]; 167 | } 168 | 169 | } 170 | }); 171 | FMDBRelease(self); 172 | return err; 173 | } 174 | #endif 175 | 176 | @end 177 | -------------------------------------------------------------------------------- /javadocset/FMResultSet.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "sqlite3.h" 3 | 4 | #ifndef __has_feature // Optional. 5 | #define __has_feature(x) 0 // Compatibility with non-clang compilers. 6 | #endif 7 | 8 | #ifndef NS_RETURNS_NOT_RETAINED 9 | #if __has_feature(attribute_ns_returns_not_retained) 10 | #define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained)) 11 | #else 12 | #define NS_RETURNS_NOT_RETAINED 13 | #endif 14 | #endif 15 | 16 | @class FMDatabase; 17 | @class FMStatement; 18 | 19 | @interface FMResultSet : NSObject { 20 | FMDatabase *_parentDB; 21 | FMStatement *_statement; 22 | 23 | NSString *_query; 24 | NSMutableDictionary *_columnNameToIndexMap; 25 | } 26 | 27 | @property (atomic, retain) NSString *query; 28 | @property (readonly) NSMutableDictionary *columnNameToIndexMap; 29 | @property (atomic, retain) FMStatement *statement; 30 | 31 | + (id)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB; 32 | 33 | - (void)close; 34 | 35 | - (void)setParentDB:(FMDatabase *)newDb; 36 | 37 | - (BOOL)next; 38 | - (BOOL)hasAnotherRow; 39 | 40 | - (int)columnCount; 41 | 42 | - (int)columnIndexForName:(NSString*)columnName; 43 | - (NSString*)columnNameForIndex:(int)columnIdx; 44 | 45 | - (int)intForColumn:(NSString*)columnName; 46 | - (int)intForColumnIndex:(int)columnIdx; 47 | 48 | - (long)longForColumn:(NSString*)columnName; 49 | - (long)longForColumnIndex:(int)columnIdx; 50 | 51 | - (long long int)longLongIntForColumn:(NSString*)columnName; 52 | - (long long int)longLongIntForColumnIndex:(int)columnIdx; 53 | 54 | - (unsigned long long int)unsignedLongLongIntForColumn:(NSString*)columnName; 55 | - (unsigned long long int)unsignedLongLongIntForColumnIndex:(int)columnIdx; 56 | 57 | - (BOOL)boolForColumn:(NSString*)columnName; 58 | - (BOOL)boolForColumnIndex:(int)columnIdx; 59 | 60 | - (double)doubleForColumn:(NSString*)columnName; 61 | - (double)doubleForColumnIndex:(int)columnIdx; 62 | 63 | - (NSString*)stringForColumn:(NSString*)columnName; 64 | - (NSString*)stringForColumnIndex:(int)columnIdx; 65 | 66 | - (NSDate*)dateForColumn:(NSString*)columnName; 67 | - (NSDate*)dateForColumnIndex:(int)columnIdx; 68 | 69 | - (NSData*)dataForColumn:(NSString*)columnName; 70 | - (NSData*)dataForColumnIndex:(int)columnIdx; 71 | 72 | - (const unsigned char *)UTF8StringForColumnIndex:(int)columnIdx; 73 | - (const unsigned char *)UTF8StringForColumnName:(NSString*)columnName; 74 | 75 | // returns one of NSNumber, NSString, NSData, or NSNull 76 | - (id)objectForColumnName:(NSString*)columnName; 77 | - (id)objectForColumnIndex:(int)columnIdx; 78 | 79 | - (id)objectForKeyedSubscript:(NSString *)columnName; 80 | - (id)objectAtIndexedSubscript:(int)columnIdx; 81 | 82 | /* 83 | If you are going to use this data after you iterate over the next row, or after you close the 84 | result set, make sure to make a copy of the data first (or just use dataForColumn:/dataForColumnIndex:) 85 | If you don't, you're going to be in a world of hurt when you try and use the data. 86 | */ 87 | - (NSData*)dataNoCopyForColumn:(NSString*)columnName NS_RETURNS_NOT_RETAINED; 88 | - (NSData*)dataNoCopyForColumnIndex:(int)columnIdx NS_RETURNS_NOT_RETAINED; 89 | 90 | - (BOOL)columnIndexIsNull:(int)columnIdx; 91 | - (BOOL)columnIsNull:(NSString*)columnName; 92 | 93 | 94 | /* Returns a dictionary of the row results mapped to case sensitive keys of the column names. */ 95 | - (NSDictionary*)resultDictionary; 96 | 97 | /* Please use resultDictionary instead. Also, beware that resultDictionary is case sensitive! */ 98 | - (NSDictionary*)resultDict __attribute__ ((deprecated)); 99 | 100 | - (void)kvcMagic:(id)object; 101 | 102 | 103 | @end 104 | 105 | -------------------------------------------------------------------------------- /javadocset/FMResultSet.m: -------------------------------------------------------------------------------- 1 | #import "FMResultSet.h" 2 | #import "FMDatabase.h" 3 | #import "unistd.h" 4 | 5 | @interface FMDatabase () 6 | - (void)resultSetDidClose:(FMResultSet *)resultSet; 7 | @end 8 | 9 | 10 | @implementation FMResultSet 11 | @synthesize query=_query; 12 | @synthesize statement=_statement; 13 | 14 | + (id)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB { 15 | 16 | FMResultSet *rs = [[FMResultSet alloc] init]; 17 | 18 | [rs setStatement:statement]; 19 | [rs setParentDB:aDB]; 20 | 21 | return FMDBReturnAutoreleased(rs); 22 | } 23 | 24 | - (void)finalize { 25 | [self close]; 26 | [super finalize]; 27 | } 28 | 29 | - (void)dealloc { 30 | [self close]; 31 | 32 | FMDBRelease(_query); 33 | _query = nil; 34 | 35 | FMDBRelease(_columnNameToIndexMap); 36 | _columnNameToIndexMap = nil; 37 | 38 | #if ! __has_feature(objc_arc) 39 | [super dealloc]; 40 | #endif 41 | } 42 | 43 | - (void)close { 44 | [_statement reset]; 45 | FMDBRelease(_statement); 46 | _statement = nil; 47 | 48 | // we don't need this anymore... (i think) 49 | //[_parentDB setInUse:NO]; 50 | [_parentDB resultSetDidClose:self]; 51 | _parentDB = nil; 52 | } 53 | 54 | - (int)columnCount { 55 | return sqlite3_column_count([_statement statement]); 56 | } 57 | 58 | - (NSMutableDictionary *)columnNameToIndexMap { 59 | if (!_columnNameToIndexMap) { 60 | int columnCount = sqlite3_column_count([_statement statement]); 61 | _columnNameToIndexMap = [[NSMutableDictionary alloc] initWithCapacity:(NSUInteger)columnCount]; 62 | int columnIdx = 0; 63 | for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { 64 | [_columnNameToIndexMap setObject:[NSNumber numberWithInt:columnIdx] 65 | forKey:[[NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)] lowercaseString]]; 66 | } 67 | } 68 | return _columnNameToIndexMap; 69 | } 70 | 71 | - (void)kvcMagic:(id)object { 72 | 73 | int columnCount = sqlite3_column_count([_statement statement]); 74 | 75 | int columnIdx = 0; 76 | for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { 77 | 78 | const char *c = (const char *)sqlite3_column_text([_statement statement], columnIdx); 79 | 80 | // check for a null row 81 | if (c) { 82 | NSString *s = [NSString stringWithUTF8String:c]; 83 | 84 | [object setValue:s forKey:[NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)]]; 85 | } 86 | } 87 | } 88 | 89 | #pragma clang diagnostic push 90 | #pragma clang diagnostic ignored "-Wdeprecated-implementations" 91 | 92 | - (NSDictionary*)resultDict { 93 | 94 | NSUInteger num_cols = (NSUInteger)sqlite3_data_count([_statement statement]); 95 | 96 | if (num_cols > 0) { 97 | NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:num_cols]; 98 | 99 | NSEnumerator *columnNames = [[self columnNameToIndexMap] keyEnumerator]; 100 | NSString *columnName = nil; 101 | while ((columnName = [columnNames nextObject])) { 102 | id objectValue = [self objectForColumnName:columnName]; 103 | [dict setObject:objectValue forKey:columnName]; 104 | } 105 | 106 | return FMDBReturnAutoreleased([dict copy]); 107 | } 108 | else { 109 | NSLog(@"Warning: There seem to be no columns in this set."); 110 | } 111 | 112 | return nil; 113 | } 114 | 115 | #pragma clang diagnostic pop 116 | 117 | - (NSDictionary*)resultDictionary { 118 | 119 | NSUInteger num_cols = (NSUInteger)sqlite3_data_count([_statement statement]); 120 | 121 | if (num_cols > 0) { 122 | NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:num_cols]; 123 | 124 | int columnCount = sqlite3_column_count([_statement statement]); 125 | 126 | int columnIdx = 0; 127 | for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { 128 | 129 | NSString *columnName = [NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)]; 130 | id objectValue = [self objectForColumnIndex:columnIdx]; 131 | [dict setObject:objectValue forKey:columnName]; 132 | } 133 | 134 | return dict; 135 | } 136 | else { 137 | NSLog(@"Warning: There seem to be no columns in this set."); 138 | } 139 | 140 | return nil; 141 | } 142 | 143 | 144 | 145 | 146 | 147 | - (BOOL)next { 148 | 149 | int rc; 150 | BOOL retry; 151 | int numberOfRetries = 0; 152 | do { 153 | retry = NO; 154 | 155 | rc = sqlite3_step([_statement statement]); 156 | 157 | if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) { 158 | // this will happen if the db is locked, like if we are doing an update or insert. 159 | // in that case, retry the step... and maybe wait just 10 milliseconds. 160 | retry = YES; 161 | if (SQLITE_LOCKED == rc) { 162 | rc = sqlite3_reset([_statement statement]); 163 | if (rc != SQLITE_LOCKED) { 164 | NSLog(@"Unexpected result from sqlite3_reset (%d) rs", rc); 165 | } 166 | } 167 | usleep(20); 168 | 169 | if ([_parentDB busyRetryTimeout] && (numberOfRetries++ > [_parentDB busyRetryTimeout])) { 170 | 171 | NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [_parentDB databasePath]); 172 | NSLog(@"Database busy"); 173 | break; 174 | } 175 | } 176 | else if (SQLITE_DONE == rc || SQLITE_ROW == rc) { 177 | // all is well, let's return. 178 | } 179 | else if (SQLITE_ERROR == rc) { 180 | NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); 181 | break; 182 | } 183 | else if (SQLITE_MISUSE == rc) { 184 | // uh oh. 185 | NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); 186 | break; 187 | } 188 | else { 189 | // wtf? 190 | NSLog(@"Unknown error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); 191 | break; 192 | } 193 | 194 | } while (retry); 195 | 196 | 197 | if (rc != SQLITE_ROW) { 198 | [self close]; 199 | } 200 | 201 | return (rc == SQLITE_ROW); 202 | } 203 | 204 | - (BOOL)hasAnotherRow { 205 | return sqlite3_errcode([_parentDB sqliteHandle]) == SQLITE_ROW; 206 | } 207 | 208 | - (int)columnIndexForName:(NSString*)columnName { 209 | columnName = [columnName lowercaseString]; 210 | 211 | NSNumber *n = [[self columnNameToIndexMap] objectForKey:columnName]; 212 | 213 | if (n) { 214 | return [n intValue]; 215 | } 216 | 217 | NSLog(@"Warning: I could not find the column named '%@'.", columnName); 218 | 219 | return -1; 220 | } 221 | 222 | 223 | 224 | - (int)intForColumn:(NSString*)columnName { 225 | return [self intForColumnIndex:[self columnIndexForName:columnName]]; 226 | } 227 | 228 | - (int)intForColumnIndex:(int)columnIdx { 229 | return sqlite3_column_int([_statement statement], columnIdx); 230 | } 231 | 232 | - (long)longForColumn:(NSString*)columnName { 233 | return [self longForColumnIndex:[self columnIndexForName:columnName]]; 234 | } 235 | 236 | - (long)longForColumnIndex:(int)columnIdx { 237 | return (long)sqlite3_column_int64([_statement statement], columnIdx); 238 | } 239 | 240 | - (long long int)longLongIntForColumn:(NSString*)columnName { 241 | return [self longLongIntForColumnIndex:[self columnIndexForName:columnName]]; 242 | } 243 | 244 | - (long long int)longLongIntForColumnIndex:(int)columnIdx { 245 | return sqlite3_column_int64([_statement statement], columnIdx); 246 | } 247 | 248 | - (unsigned long long int)unsignedLongLongIntForColumn:(NSString*)columnName { 249 | return [self unsignedLongLongIntForColumnIndex:[self columnIndexForName:columnName]]; 250 | } 251 | 252 | - (unsigned long long int)unsignedLongLongIntForColumnIndex:(int)columnIdx { 253 | return (unsigned long long int)[self longLongIntForColumnIndex:columnIdx]; 254 | } 255 | 256 | - (BOOL)boolForColumn:(NSString*)columnName { 257 | return [self boolForColumnIndex:[self columnIndexForName:columnName]]; 258 | } 259 | 260 | - (BOOL)boolForColumnIndex:(int)columnIdx { 261 | return ([self intForColumnIndex:columnIdx] != 0); 262 | } 263 | 264 | - (double)doubleForColumn:(NSString*)columnName { 265 | return [self doubleForColumnIndex:[self columnIndexForName:columnName]]; 266 | } 267 | 268 | - (double)doubleForColumnIndex:(int)columnIdx { 269 | return sqlite3_column_double([_statement statement], columnIdx); 270 | } 271 | 272 | - (NSString*)stringForColumnIndex:(int)columnIdx { 273 | 274 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 275 | return nil; 276 | } 277 | 278 | const char *c = (const char *)sqlite3_column_text([_statement statement], columnIdx); 279 | 280 | if (!c) { 281 | // null row. 282 | return nil; 283 | } 284 | 285 | return [NSString stringWithUTF8String:c]; 286 | } 287 | 288 | - (NSString*)stringForColumn:(NSString*)columnName { 289 | return [self stringForColumnIndex:[self columnIndexForName:columnName]]; 290 | } 291 | 292 | - (NSDate*)dateForColumn:(NSString*)columnName { 293 | return [self dateForColumnIndex:[self columnIndexForName:columnName]]; 294 | } 295 | 296 | - (NSDate*)dateForColumnIndex:(int)columnIdx { 297 | 298 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 299 | return nil; 300 | } 301 | 302 | return [NSDate dateWithTimeIntervalSince1970:[self doubleForColumnIndex:columnIdx]]; 303 | } 304 | 305 | 306 | - (NSData*)dataForColumn:(NSString*)columnName { 307 | return [self dataForColumnIndex:[self columnIndexForName:columnName]]; 308 | } 309 | 310 | - (NSData*)dataForColumnIndex:(int)columnIdx { 311 | 312 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 313 | return nil; 314 | } 315 | 316 | int dataSize = sqlite3_column_bytes([_statement statement], columnIdx); 317 | 318 | NSMutableData *data = [NSMutableData dataWithLength:(NSUInteger)dataSize]; 319 | 320 | memcpy([data mutableBytes], sqlite3_column_blob([_statement statement], columnIdx), dataSize); 321 | 322 | return data; 323 | } 324 | 325 | 326 | - (NSData*)dataNoCopyForColumn:(NSString*)columnName { 327 | return [self dataNoCopyForColumnIndex:[self columnIndexForName:columnName]]; 328 | } 329 | 330 | - (NSData*)dataNoCopyForColumnIndex:(int)columnIdx { 331 | 332 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 333 | return nil; 334 | } 335 | 336 | int dataSize = sqlite3_column_bytes([_statement statement], columnIdx); 337 | 338 | NSData *data = [NSData dataWithBytesNoCopy:(void *)sqlite3_column_blob([_statement statement], columnIdx) length:(NSUInteger)dataSize freeWhenDone:NO]; 339 | 340 | return data; 341 | } 342 | 343 | 344 | - (BOOL)columnIndexIsNull:(int)columnIdx { 345 | return sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL; 346 | } 347 | 348 | - (BOOL)columnIsNull:(NSString*)columnName { 349 | return [self columnIndexIsNull:[self columnIndexForName:columnName]]; 350 | } 351 | 352 | - (const unsigned char *)UTF8StringForColumnIndex:(int)columnIdx { 353 | 354 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 355 | return nil; 356 | } 357 | 358 | return sqlite3_column_text([_statement statement], columnIdx); 359 | } 360 | 361 | - (const unsigned char *)UTF8StringForColumnName:(NSString*)columnName { 362 | return [self UTF8StringForColumnIndex:[self columnIndexForName:columnName]]; 363 | } 364 | 365 | - (id)objectForColumnIndex:(int)columnIdx { 366 | int columnType = sqlite3_column_type([_statement statement], columnIdx); 367 | 368 | id returnValue = nil; 369 | 370 | if (columnType == SQLITE_INTEGER) { 371 | returnValue = [NSNumber numberWithLongLong:[self longLongIntForColumnIndex:columnIdx]]; 372 | } 373 | else if (columnType == SQLITE_FLOAT) { 374 | returnValue = [NSNumber numberWithDouble:[self doubleForColumnIndex:columnIdx]]; 375 | } 376 | else if (columnType == SQLITE_BLOB) { 377 | returnValue = [self dataForColumnIndex:columnIdx]; 378 | } 379 | else { 380 | //default to a string for everything else 381 | returnValue = [self stringForColumnIndex:columnIdx]; 382 | } 383 | 384 | if (returnValue == nil) { 385 | returnValue = [NSNull null]; 386 | } 387 | 388 | return returnValue; 389 | } 390 | 391 | - (id)objectForColumnName:(NSString*)columnName { 392 | return [self objectForColumnIndex:[self columnIndexForName:columnName]]; 393 | } 394 | 395 | // returns autoreleased NSString containing the name of the column in the result set 396 | - (NSString*)columnNameForIndex:(int)columnIdx { 397 | return [NSString stringWithUTF8String: sqlite3_column_name([_statement statement], columnIdx)]; 398 | } 399 | 400 | - (void)setParentDB:(FMDatabase *)newDb { 401 | _parentDB = newDb; 402 | } 403 | 404 | - (id)objectAtIndexedSubscript:(int)columnIdx { 405 | return [self objectForColumnIndex:columnIdx]; 406 | } 407 | 408 | - (id)objectForKeyedSubscript:(NSString *)columnName { 409 | return [self objectForColumnName:columnName]; 410 | } 411 | 412 | 413 | @end 414 | -------------------------------------------------------------------------------- /javadocset/LICENSE.txt: -------------------------------------------------------------------------------- 1 | If you are using fmdb in your project, I'd love to hear about it. Let me 2 | know at gus@flyingmeat.com. 3 | 4 | In short, this is the MIT License. 5 | 6 | Copyright (c) 2008 Flying Meat Inc. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. -------------------------------------------------------------------------------- /javadocset/javadocset-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'javadocset' target in the 'javadocset' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /javadocset/javadocset.1: -------------------------------------------------------------------------------- 1 | .\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. 2 | .\"See Also: 3 | .\"man mdoc.samples for a complete listing of options 4 | .\"man mdoc for the short list of editing options 5 | .\"/usr/share/misc/mdoc.template 6 | .Dd ___DATE___ \" DATE 7 | .Dt ___PACKAGENAME___ 1 \" Program name and manual section number 8 | .Os Darwin 9 | .Sh NAME \" Section Header - required - don't modify 10 | .Nm ___PACKAGENAME___, 11 | .\" The following lines are read in generating the apropos(man -k) database. Use only key 12 | .\" words here as the database is built based on the words here and in the .ND line. 13 | .Nm Other_name_for_same_program(), 14 | .Nm Yet another name for the same program. 15 | .\" Use .Nm macro to designate other names for the documented program. 16 | .Nd This line parsed for whatis database. 17 | .Sh SYNOPSIS \" Section Header - required - don't modify 18 | .Nm 19 | .Op Fl abcd \" [-abcd] 20 | .Op Fl a Ar path \" [-a path] 21 | .Op Ar file \" [file] 22 | .Op Ar \" [file ...] 23 | .Ar arg0 \" Underlined argument - use .Ar anywhere to underline 24 | arg2 ... \" Arguments 25 | .Sh DESCRIPTION \" Section Header - required - don't modify 26 | Use the .Nm macro to refer to your program throughout the man page like such: 27 | .Nm 28 | Underlining is accomplished with the .Ar macro like this: 29 | .Ar underlined text . 30 | .Pp \" Inserts a space 31 | A list of items with descriptions: 32 | .Bl -tag -width -indent \" Begins a tagged list 33 | .It item a \" Each item preceded by .It macro 34 | Description of item a 35 | .It item b 36 | Description of item b 37 | .El \" Ends the list 38 | .Pp 39 | A list of flags and their descriptions: 40 | .Bl -tag -width -indent \" Differs from above in tag removed 41 | .It Fl a \"-a flag as a list item 42 | Description of -a flag 43 | .It Fl b 44 | Description of -b flag 45 | .El \" Ends the list 46 | .Pp 47 | .\" .Sh ENVIRONMENT \" May not be needed 48 | .\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 49 | .\" .It Ev ENV_VAR_1 50 | .\" Description of ENV_VAR_1 51 | .\" .It Ev ENV_VAR_2 52 | .\" Description of ENV_VAR_2 53 | .\" .El 54 | .Sh FILES \" File used or created by the topic of the man page 55 | .Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact 56 | .It Pa /usr/share/file_name 57 | FILE_1 description 58 | .It Pa /Users/joeuser/Library/really_long_file_name 59 | FILE_2 description 60 | .El \" Ends the list 61 | .\" .Sh DIAGNOSTICS \" May not be needed 62 | .\" .Bl -diag 63 | .\" .It Diagnostic Tag 64 | .\" Diagnostic informtion here. 65 | .\" .It Diagnostic Tag 66 | .\" Diagnostic informtion here. 67 | .\" .El 68 | .Sh SEE ALSO 69 | .\" List links in ascending order by section, alphabetically within a section. 70 | .\" Please do not reference files that do not exist without filing a bug report 71 | .Xr a 1 , 72 | .Xr b 1 , 73 | .Xr c 1 , 74 | .Xr a 2 , 75 | .Xr b 2 , 76 | .Xr a 3 , 77 | .Xr b 3 78 | .\" .Sh BUGS \" Document known, unremedied bugs 79 | .\" .Sh HISTORY \" Document history if command behaves in a unique manner -------------------------------------------------------------------------------- /javadocset/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "DHIndexer.h" 3 | 4 | int main(int argc, const char * argv[]) 5 | { 6 | @autoreleasepool { 7 | DHIndexer *indexer = [[DHIndexer alloc] init]; 8 | if(!indexer) 9 | { 10 | return 1; 11 | } 12 | while([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) 13 | { 14 | } 15 | } 16 | return 0; 17 | } 18 | 19 | --------------------------------------------------------------------------------