From ec1eaf81a79a331efbcd9173ba4712b99374c557 Mon Sep 17 00:00:00 2001 From: Theo Date: Fri, 14 Jul 2017 10:24:40 +0100 Subject: [PATCH] Re-structured the repo. --- .gitignore | 2 +- build.xml | 34 + lib/DDSUtils.jar | Bin 0 -> 213600 bytes lib/gson-2.8.1.jar | Bin 0 -> 232620 bytes nbproject/build-impl.xml | 32 + nbproject/genfiles.properties | 4 +- nbproject/project.properties | 20 +- nbproject/project.xml | 18 + .../java/io/airlift/compress/Compressor.java | 28 - .../io/airlift/compress/Decompressor.java | 28 - .../compress/MalformedInputException.java | 36 - .../airlift/compress/lz4/Lz4Compressor.java | 104 - .../io/airlift/compress/lz4/Lz4Constants.java | 26 - .../airlift/compress/lz4/Lz4Decompressor.java | 90 - .../compress/lz4/Lz4RawCompressor.java | 313 - .../compress/lz4/Lz4RawDecompressor.java | 185 - .../io/airlift/compress/lz4/UnsafeUtil.java | 57 - .../compression/ARGBBufferDecompressor.java | 53 - .../compression/BufferDecompressor.java | 27 - .../compression/DXTBufferCompressor.java | 278 - .../compression/DXTBufferDecompressor.java | 126 - src/ddsutils/compression/DXTCompression.java | 109 - src/ddsutils/ddsutil/BIUtil.java | 96 - src/ddsutils/ddsutil/ByteBufferedImage.java | 218 - src/ddsutils/ddsutil/DDSUtil.java | 302 - src/ddsutils/ddsutil/ImageOperations.java | 242 - src/ddsutils/ddsutil/ImageRescaler.java | 68 - src/ddsutils/ddsutil/MipMapsUtil.java | 51 - .../ddsutil/NonCubicDimensionException.java | 24 - src/ddsutils/ddsutil/PixelFormats.java | 187 - src/ddsutils/ddsutil/Rescaler.java | 24 - src/ddsutils/ddsutil/TextureFactory.java | 30 - src/ddsutils/jogl/DDSImage.java | 1029 -- src/ddsutils/jogl/TEXImage.java | 528 - src/ddsutils/jogl/ddsimage-class-bug | 77 - src/ddsutils/model/AbstractTextureImage.java | 234 - src/ddsutils/model/AbstractTextureMap.java | 48 - src/ddsutils/model/DDSCubeImageFile.java | 86 - src/ddsutils/model/DDSFile.java | 255 - src/ddsutils/model/MipMaps.java | 322 - src/ddsutils/model/SingleTextureMap.java | 76 - src/ddsutils/model/TEXFile.java | 135 - src/ddsutils/model/TextureImage.java | 192 - src/ddsutils/model/TextureMap.java | 63 - src/ddsutils/util/Debug.java | 38 - src/ddsutils/util/FileUtil.java | 129 - src/ddsutils/util/ImageIOUtils.java | 22 - src/ddsutils/util/ImageUtils.java | 312 - src/ddsutils/util/Stopwatch.java | 39 - .../google/gson/DefaultDateTypeAdapter.java | 134 - .../com/google/gson/ExclusionStrategy.java | 109 - .../java/com/google/gson/FieldAttributes.java | 157 - .../com/google/gson/FieldNamingPolicy.java | 166 - .../com/google/gson/FieldNamingStrategy.java | 40 - src/gson/main/java/com/google/gson/Gson.java | 989 -- .../java/com/google/gson/GsonBuilder.java | 597 - .../java/com/google/gson/InstanceCreator.java | 92 - .../main/java/com/google/gson/JsonArray.java | 384 - .../gson/JsonDeserializationContext.java | 44 - .../com/google/gson/JsonDeserializer.java | 91 - .../java/com/google/gson/JsonElement.java | 331 - .../java/com/google/gson/JsonIOException.java | 45 - .../main/java/com/google/gson/JsonNull.java | 67 - .../main/java/com/google/gson/JsonObject.java | 218 - .../com/google/gson/JsonParseException.java | 64 - .../main/java/com/google/gson/JsonParser.java | 93 - .../java/com/google/gson/JsonPrimitive.java | 345 - .../google/gson/JsonSerializationContext.java | 49 - .../java/com/google/gson/JsonSerializer.java | 89 - .../com/google/gson/JsonStreamParser.java | 122 - .../com/google/gson/JsonSyntaxException.java | 47 - .../google/gson/LongSerializationPolicy.java | 58 - .../java/com/google/gson/TypeAdapter.java | 290 - .../com/google/gson/TypeAdapterFactory.java | 170 - .../com/google/gson/annotations/Expose.java | 81 - .../google/gson/annotations/JsonAdapter.java | 104 - .../gson/annotations/SerializedName.java | 93 - .../com/google/gson/annotations/Since.java | 63 - .../com/google/gson/annotations/Until.java | 68 - .../google/gson/annotations/package-info.java | 6 - .../gson/internal/$Gson$Preconditions.java | 49 - .../com/google/gson/internal/$Gson$Types.java | 599 - .../gson/internal/ConstructorConstructor.java | 236 - .../com/google/gson/internal/Excluder.java | 251 - .../internal/JsonReaderInternalAccess.java | 32 - .../gson/internal/LazilyParsedNumber.java | 96 - .../gson/internal/LinkedHashTreeMap.java | 864 -- .../google/gson/internal/LinkedTreeMap.java | 630 - .../gson/internal/ObjectConstructor.java | 33 - .../com/google/gson/internal/Primitives.java | 119 - .../com/google/gson/internal/Streams.java | 120 - .../google/gson/internal/UnsafeAllocator.java | 123 - .../gson/internal/bind/ArrayTypeAdapter.java | 99 - .../bind/CollectionTypeAdapterFactory.java | 102 - .../gson/internal/bind/DateTypeAdapter.java | 88 - ...onAdapterAnnotationTypeAdapterFactory.java | 83 - .../gson/internal/bind/JsonTreeReader.java | 320 - .../gson/internal/bind/JsonTreeWriter.java | 208 - .../internal/bind/MapTypeAdapterFactory.java | 264 - .../gson/internal/bind/ObjectTypeAdapter.java | 109 - .../bind/ReflectiveTypeAdapterFactory.java | 252 - .../internal/bind/SqlDateTypeAdapter.java | 67 - .../gson/internal/bind/TimeTypeAdapter.java | 66 - .../gson/internal/bind/TreeTypeAdapter.java | 165 - .../bind/TypeAdapterRuntimeTypeWrapper.java | 82 - .../gson/internal/bind/TypeAdapters.java | 907 -- .../gson/internal/bind/util/ISO8601Utils.java | 352 - .../google/gson/internal/package-info.java | 7 - .../java/com/google/gson/package-info.java | 11 - .../com/google/gson/reflect/TypeToken.java | 320 - .../com/google/gson/reflect/package-info.java | 6 - .../com/google/gson/stream/JsonReader.java | 1617 -- .../com/google/gson/stream/JsonScope.java | 71 - .../com/google/gson/stream/JsonToken.java | 85 - .../com/google/gson/stream/JsonWriter.java | 659 - .../gson/stream/MalformedJsonException.java | 44 - .../gr/zdimensions/jsquish/ColourBlock.java | 191 - .../gr/zdimensions/jsquish/ColourSet.java | 125 - .../zdimensions/jsquish/CompressorAlpha.java | 309 - .../jsquish/CompressorCluster.java | 433 - .../jsquish/CompressorColourFit.java | 76 - .../zdimensions/jsquish/CompressorRange.java | 228 - .../jsquish/CompressorSingleColour.java | 149 - .../java/gr/zdimensions/jsquish/Matrix.java | 239 - .../jsquish/SingleColourLookup3.java | 550 - .../jsquish/SingleColourLookup4.java | 550 - .../java/gr/zdimensions/jsquish/Squish.java | 261 - .../main/java/gr/zdimensions/jsquish/Vec.java | 139 - src/lev/FastByteArrayInputStream.java | 91 - src/lev/FastByteArrayOutputStream.java | 119 - src/lev/LByteChannel.java | 134 - src/lev/LByteSearcher.java | 113 - src/lev/LExport.java | 146 - src/lev/LFlags.java | 164 - src/lev/LGlobal.java | 28 - src/lev/LImport.java | 450 - src/lev/LInChannel.java | 235 - src/lev/LMergable.java | 17 - src/lev/LMergeList.java | 77 - src/lev/LMergeMap.java | 262 - src/lev/LOutChannel.java | 149 - src/lev/LOutFile.java | 61 - src/lev/LPair.java | 34 - src/lev/LShrinkArray.java | 211 - src/lev/LStringSearcher.java | 106 - src/lev/Ln.java | 1796 --- src/lev/debug/LDebug.java | 279 - src/lev/debug/LLogger.java | 286 - src/lev/gui/JAutoComboBox.java | 116 - src/lev/gui/JAutoTextField.java | 209 - src/lev/gui/LAreaChart.java | 137 - src/lev/gui/LButton.java | 198 - src/lev/gui/LCenterPanel.java | 43 - src/lev/gui/LChart.java | 77 - src/lev/gui/LCheckBox.java | 255 - src/lev/gui/LCheckBoxConfig.java | 232 - src/lev/gui/LColorSetting.java | 187 - src/lev/gui/LComboBox.java | 255 - src/lev/gui/LComboSearchBox.java | 233 - src/lev/gui/LComponent.java | 102 - src/lev/gui/LDoubleSetting.java | 150 - src/lev/gui/LDoubleSpinner.java | 125 - src/lev/gui/LEditorPane.java | 150 - src/lev/gui/LFileTree.java | 122 - src/lev/gui/LFrame.java | 140 - src/lev/gui/LHTMLPane.java | 47 - src/lev/gui/LHelpComponent.java | 242 - src/lev/gui/LHelpPanel.java | 320 - src/lev/gui/LImagePane.java | 222 - src/lev/gui/LLabel.java | 154 - src/lev/gui/LList.java | 201 - src/lev/gui/LMenuItem.java | 73 - src/lev/gui/LNumericSetting.java | 169 - src/lev/gui/LPanel.java | 198 - src/lev/gui/LProgressBar.java | 234 - src/lev/gui/LProgressBarFrame.java | 230 - src/lev/gui/LProgressBarInterface.java | 82 - src/lev/gui/LSaveFile.java | 678 - src/lev/gui/LScrollPane.java | 53 - src/lev/gui/LSlider.java | 103 - src/lev/gui/LSpinner.java | 124 - src/lev/gui/LSwingTreeNode.java | 100 - src/lev/gui/LSwingWorker.java | 34 - src/lev/gui/LTextArea.java | 239 - src/lev/gui/LTextField.java | 160 - src/lev/gui/LTextPane.java | 279 - src/lev/gui/LTree.java | 226 - src/lev/gui/LUserSetting.java | 168 - src/lev/gui/Lg.java | 158 - src/lev/gui/SaveBool.java | 36 - src/lev/gui/SaveColor.java | 57 - src/lev/gui/SaveDouble.java | 40 - src/lev/gui/SaveEnum.java | 43 - src/lev/gui/SaveFloat.java | 34 - src/lev/gui/SaveInt.java | 34 - src/lev/gui/SaveString.java | 38 - src/lev/gui/SaveStringList.java | 59 - src/lev/gui/Setting.java | 235 - src/lev/gui/SortedListModel.java | 121 - src/lev/gui/resources/ArrowLeft.png | Bin 3015 -> 0 bytes src/lev/gui/resources/ArrowLeftDark.png | Bin 2975 -> 0 bytes src/lev/gui/resources/ArrowRight.png | Bin 3043 -> 0 bytes src/lev/gui/resources/ArrowRightDark.png | Bin 2973 -> 0 bytes src/lev/gui/resources/LFonts.java | 89 - src/lev/gui/resources/LImages.java | 57 - src/lev/gui/resources/MyriadPro-Regular.ttf | Bin 94884 -> 0 bytes src/lev/gui/resources/NEUROPOL.ttf | Bin 54916 -> 0 bytes src/lev/gui/resources/OLEO.TTF | Bin 32732 -> 0 bytes src/lev/gui/resources/OptimusPrinceps.ttf | Bin 41416 -> 0 bytes .../gui/resources/OptimusPrincepsSemiBold.ttf | Bin 57296 -> 0 bytes src/lev/gui/resources/Typo3-Medium.ttf | Bin 26052 -> 0 bytes src/lev/gui/resources/multipurpose.png | Bin 906448 -> 0 bytes src/lev/gui/resources/myriadwebpro-bold.ttf | Bin 103512 -> 0 bytes .../gui/resources/reasonSystem-Regular.ttf | Bin 66552 -> 0 bytes src/lev/gui/resources/typo3Normal.ttf | Bin 34956 -> 0 bytes src/skyproc/ALCH.java | 341 - src/skyproc/AMMO.java | 275 - src/skyproc/ARMA.java | 421 - src/skyproc/ARMO.java | 450 - src/skyproc/AVIF.java | 250 - src/skyproc/AltTextures.java | 262 - src/skyproc/BOOK.java | 387 - src/skyproc/BSA.java | 990 -- src/skyproc/BodyTemplate.java | 249 - src/skyproc/COBJ.java | 165 - src/skyproc/CONT.java | 192 - src/skyproc/Condition.java | 2870 ---- src/skyproc/ConditionBase.java | 189 - src/skyproc/ConditionOption.java | 1547 -- src/skyproc/Consistency.java | 548 - src/skyproc/DIAL.java | 158 - src/skyproc/DLBR.java | 73 - src/skyproc/DLVW.java | 88 - src/skyproc/DestructionData.java | 112 - src/skyproc/ECZN.java | 260 - src/skyproc/ENCH.java | 331 - src/skyproc/EmbeddedScripts.java | 368 - src/skyproc/FACT.java | 61 - src/skyproc/FLST.java | 108 - src/skyproc/FormID.java | 333 - src/skyproc/GLOB.java | 172 - src/skyproc/GMST.java | 12427 ---------------- src/skyproc/GRUP.java | 369 - src/skyproc/GRUPRecursive.java | 34 - src/skyproc/GRUP_TYPE.java | 186 - src/skyproc/HDPT.java | 239 - src/skyproc/IMGS.java | 651 - src/skyproc/INFO.java | 687 - src/skyproc/INGR.java | 361 - src/skyproc/ItemListing.java | 110 - src/skyproc/KYWD.java | 59 - src/skyproc/KeywordSet.java | 172 - src/skyproc/LGTM.java | 209 - src/skyproc/LIGH.java | 544 - src/skyproc/LVLI.java | 49 - src/skyproc/LVLN.java | 91 - src/skyproc/LeveledEntry.java | 176 - src/skyproc/LeveledRecord.java | 482 - src/skyproc/MGEF.java | 1315 -- src/skyproc/MISC.java | 245 - src/skyproc/MagicEffectRef.java | 225 - src/skyproc/MagicItem.java | 207 - src/skyproc/MajorRecord.java | 533 - src/skyproc/MajorRecordDescription.java | 46 - src/skyproc/MajorRecordNamed.java | 42 - src/skyproc/Mod.java | 1699 --- src/skyproc/ModExporter.java | 45 - src/skyproc/ModListing.java | 263 - src/skyproc/Model.java | 77 - src/skyproc/NIF.java | 496 - src/skyproc/NPC_.java | 2880 ---- src/skyproc/NiftyFunc.java | 767 - src/skyproc/OTFT.java | 80 - src/skyproc/Owner.java | 87 - src/skyproc/PERK.java | 281 - src/skyproc/PROJ.java | 696 - src/skyproc/QUST.java | 1325 -- src/skyproc/RACE.java | 2008 --- src/skyproc/RGB.java | 24 - src/skyproc/RGBA.java | 29 - src/skyproc/Record.java | 213 - src/skyproc/RecordFileChannel.java | 84 - src/skyproc/RecordShrinkArray.java | 136 - src/skyproc/SCRL.java | 330 - src/skyproc/SPDatabase.java | 342 - src/skyproc/SPEL.java | 298 - src/skyproc/SPGlobal.java | 776 - src/skyproc/SPImporter.java | 1046 -- src/skyproc/SPLogger.java | 58 - src/skyproc/STAT.java | 171 - src/skyproc/SUMMergerProgram.java | 199 - src/skyproc/ScriptPackage.java | 191 - src/skyproc/ScriptProperty.java | 684 - src/skyproc/ScriptRef.java | 375 - src/skyproc/SkyProcSave.java | 20 - src/skyproc/SkyProcTester.java | 241 - src/skyproc/StringNonNull.java | 113 - src/skyproc/StringNull.java | 40 - src/skyproc/SubData.java | 148 - src/skyproc/SubFlag.java | 88 - src/skyproc/SubFloat.java | 91 - src/skyproc/SubForm.java | 177 - src/skyproc/SubFormArray.java | 152 - src/skyproc/SubFormData.java | 148 - src/skyproc/SubFormInt.java | 160 - src/skyproc/SubInt.java | 119 - src/skyproc/SubIntSigned.java | 130 - src/skyproc/SubList.java | 262 - src/skyproc/SubListCounted.java | 79 - src/skyproc/SubListMulti.java | 68 - src/skyproc/SubListSorted.java | 81 - src/skyproc/SubMarkerSet.java | 150 - src/skyproc/SubPrototype.java | 97 - src/skyproc/SubRGB.java | 142 - src/skyproc/SubRGBshort.java | 152 - src/skyproc/SubRecord.java | 86 - src/skyproc/SubRecordTyped.java | 31 - src/skyproc/SubRecords.java | 308 - src/skyproc/SubRecordsCopied.java | 110 - src/skyproc/SubRecordsDerived.java | 114 - src/skyproc/SubRecordsStream.java | 168 - src/skyproc/SubShell.java | 94 - src/skyproc/SubShellBulkNumber.java | 38 - src/skyproc/SubShellBulkType.java | 68 - src/skyproc/SubString.java | 131 - src/skyproc/SubStringNonNull.java | 50 - src/skyproc/SubStringPointer.java | 189 - src/skyproc/TXST.java | 280 - src/skyproc/VTYP.java | 85 - src/skyproc/WEAP.java | 1165 -- src/skyproc/WTHR.java | 93 - src/skyproc/exceptions/BadMod.java | 26 - src/skyproc/exceptions/BadParameter.java | 26 - src/skyproc/exceptions/BadRecord.java | 26 - src/skyproc/exceptions/MissingMaster.java | 26 - src/skyproc/exceptions/NotFound.java | 26 - src/skyproc/exceptions/Uninitialized.java | 27 - src/skyproc/genenums/ActorValue.java | 1117 -- src/skyproc/genenums/ArmorType.java | 25 - src/skyproc/genenums/Axis.java | 40 - src/skyproc/genenums/CastType.java | 26 - src/skyproc/genenums/CastingSource.java | 29 - src/skyproc/genenums/CrimeType.java | 40 - src/skyproc/genenums/DeliveryType.java | 33 - src/skyproc/genenums/EmotionType.java | 44 - src/skyproc/genenums/FavorLevel.java | 28 - src/skyproc/genenums/FirstPersonFlags.java | 171 - src/skyproc/genenums/Gender.java | 20 - src/skyproc/genenums/HeadPartFlags.java | 19 - src/skyproc/genenums/MovementType.java | 73 - src/skyproc/genenums/Perspective.java | 21 - src/skyproc/genenums/Skill.java | 155 - src/skyproc/genenums/SoundVolume.java | 29 - src/skyproc/genenums/WardState.java | 24 - src/skyproc/gui/BackToSUM.png | Bin 8955 -> 0 bytes src/skyproc/gui/BackToSUMdark.png | Bin 8939 -> 0 bytes src/skyproc/gui/LFormIDPicker.java | 201 - src/skyproc/gui/Open Settings Collapsed.png | Bin 5014 -> 0 bytes src/skyproc/gui/Open Settings.png | Bin 6512 -> 0 bytes src/skyproc/gui/SPDefaultGUI.java | 204 - src/skyproc/gui/SPList.java | 109 - src/skyproc/gui/SPMainMenuConfig.java | 77 - src/skyproc/gui/SPMainMenuPanel.java | 265 - src/skyproc/gui/SPProgressBarFrame.java | 49 - src/skyproc/gui/SPProgressBarPlug.java | 252 - src/skyproc/gui/SPQuestionPanel.java | 237 - src/skyproc/gui/SPSettingDefaultsPanel.java | 144 - src/skyproc/gui/SPSettingPanel.java | 232 - src/skyproc/gui/SPStringList.java | 65 - src/skyproc/gui/SUM program.png | Bin 43431 -> 0 bytes src/skyproc/gui/SUM.java | 172 - src/skyproc/gui/SUMGUI.java | 1234 -- src/skyproc/gui/SUMprogram.java | 1085 -- src/skyproc/gui/SaveFormList.java | 51 - src/skyproc/gui/SkyProc Logo Small.png | Bin 8200 -> 0 bytes src/skyproc/gui/background.jpg | Bin 302966 -> 0 bytes 376 files changed, 97 insertions(+), 95133 deletions(-) create mode 100644 lib/DDSUtils.jar create mode 100644 lib/gson-2.8.1.jar delete mode 100644 src/aircompressor/main/java/io/airlift/compress/Compressor.java delete mode 100644 src/aircompressor/main/java/io/airlift/compress/Decompressor.java delete mode 100644 src/aircompressor/main/java/io/airlift/compress/MalformedInputException.java delete mode 100644 src/aircompressor/main/java/io/airlift/compress/lz4/Lz4Compressor.java delete mode 100644 src/aircompressor/main/java/io/airlift/compress/lz4/Lz4Constants.java delete mode 100644 src/aircompressor/main/java/io/airlift/compress/lz4/Lz4Decompressor.java delete mode 100644 src/aircompressor/main/java/io/airlift/compress/lz4/Lz4RawCompressor.java delete mode 100644 src/aircompressor/main/java/io/airlift/compress/lz4/Lz4RawDecompressor.java delete mode 100644 src/aircompressor/main/java/io/airlift/compress/lz4/UnsafeUtil.java delete mode 100644 src/ddsutils/compression/ARGBBufferDecompressor.java delete mode 100644 src/ddsutils/compression/BufferDecompressor.java delete mode 100644 src/ddsutils/compression/DXTBufferCompressor.java delete mode 100644 src/ddsutils/compression/DXTBufferDecompressor.java delete mode 100644 src/ddsutils/compression/DXTCompression.java delete mode 100644 src/ddsutils/ddsutil/BIUtil.java delete mode 100644 src/ddsutils/ddsutil/ByteBufferedImage.java delete mode 100644 src/ddsutils/ddsutil/DDSUtil.java delete mode 100644 src/ddsutils/ddsutil/ImageOperations.java delete mode 100644 src/ddsutils/ddsutil/ImageRescaler.java delete mode 100644 src/ddsutils/ddsutil/MipMapsUtil.java delete mode 100644 src/ddsutils/ddsutil/NonCubicDimensionException.java delete mode 100644 src/ddsutils/ddsutil/PixelFormats.java delete mode 100644 src/ddsutils/ddsutil/Rescaler.java delete mode 100644 src/ddsutils/ddsutil/TextureFactory.java delete mode 100644 src/ddsutils/jogl/DDSImage.java delete mode 100644 src/ddsutils/jogl/TEXImage.java delete mode 100644 src/ddsutils/jogl/ddsimage-class-bug delete mode 100644 src/ddsutils/model/AbstractTextureImage.java delete mode 100644 src/ddsutils/model/AbstractTextureMap.java delete mode 100644 src/ddsutils/model/DDSCubeImageFile.java delete mode 100644 src/ddsutils/model/DDSFile.java delete mode 100644 src/ddsutils/model/MipMaps.java delete mode 100644 src/ddsutils/model/SingleTextureMap.java delete mode 100644 src/ddsutils/model/TEXFile.java delete mode 100644 src/ddsutils/model/TextureImage.java delete mode 100644 src/ddsutils/model/TextureMap.java delete mode 100644 src/ddsutils/util/Debug.java delete mode 100644 src/ddsutils/util/FileUtil.java delete mode 100644 src/ddsutils/util/ImageIOUtils.java delete mode 100644 src/ddsutils/util/ImageUtils.java delete mode 100644 src/ddsutils/util/Stopwatch.java delete mode 100644 src/gson/main/java/com/google/gson/DefaultDateTypeAdapter.java delete mode 100644 src/gson/main/java/com/google/gson/ExclusionStrategy.java delete mode 100644 src/gson/main/java/com/google/gson/FieldAttributes.java delete mode 100644 src/gson/main/java/com/google/gson/FieldNamingPolicy.java delete mode 100644 src/gson/main/java/com/google/gson/FieldNamingStrategy.java delete mode 100644 src/gson/main/java/com/google/gson/Gson.java delete mode 100644 src/gson/main/java/com/google/gson/GsonBuilder.java delete mode 100644 src/gson/main/java/com/google/gson/InstanceCreator.java delete mode 100644 src/gson/main/java/com/google/gson/JsonArray.java delete mode 100644 src/gson/main/java/com/google/gson/JsonDeserializationContext.java delete mode 100644 src/gson/main/java/com/google/gson/JsonDeserializer.java delete mode 100644 src/gson/main/java/com/google/gson/JsonElement.java delete mode 100644 src/gson/main/java/com/google/gson/JsonIOException.java delete mode 100644 src/gson/main/java/com/google/gson/JsonNull.java delete mode 100644 src/gson/main/java/com/google/gson/JsonObject.java delete mode 100644 src/gson/main/java/com/google/gson/JsonParseException.java delete mode 100644 src/gson/main/java/com/google/gson/JsonParser.java delete mode 100644 src/gson/main/java/com/google/gson/JsonPrimitive.java delete mode 100644 src/gson/main/java/com/google/gson/JsonSerializationContext.java delete mode 100644 src/gson/main/java/com/google/gson/JsonSerializer.java delete mode 100644 src/gson/main/java/com/google/gson/JsonStreamParser.java delete mode 100644 src/gson/main/java/com/google/gson/JsonSyntaxException.java delete mode 100644 src/gson/main/java/com/google/gson/LongSerializationPolicy.java delete mode 100644 src/gson/main/java/com/google/gson/TypeAdapter.java delete mode 100644 src/gson/main/java/com/google/gson/TypeAdapterFactory.java delete mode 100644 src/gson/main/java/com/google/gson/annotations/Expose.java delete mode 100644 src/gson/main/java/com/google/gson/annotations/JsonAdapter.java delete mode 100644 src/gson/main/java/com/google/gson/annotations/SerializedName.java delete mode 100644 src/gson/main/java/com/google/gson/annotations/Since.java delete mode 100644 src/gson/main/java/com/google/gson/annotations/Until.java delete mode 100644 src/gson/main/java/com/google/gson/annotations/package-info.java delete mode 100644 src/gson/main/java/com/google/gson/internal/$Gson$Preconditions.java delete mode 100644 src/gson/main/java/com/google/gson/internal/$Gson$Types.java delete mode 100644 src/gson/main/java/com/google/gson/internal/ConstructorConstructor.java delete mode 100644 src/gson/main/java/com/google/gson/internal/Excluder.java delete mode 100644 src/gson/main/java/com/google/gson/internal/JsonReaderInternalAccess.java delete mode 100644 src/gson/main/java/com/google/gson/internal/LazilyParsedNumber.java delete mode 100644 src/gson/main/java/com/google/gson/internal/LinkedHashTreeMap.java delete mode 100644 src/gson/main/java/com/google/gson/internal/LinkedTreeMap.java delete mode 100644 src/gson/main/java/com/google/gson/internal/ObjectConstructor.java delete mode 100644 src/gson/main/java/com/google/gson/internal/Primitives.java delete mode 100644 src/gson/main/java/com/google/gson/internal/Streams.java delete mode 100644 src/gson/main/java/com/google/gson/internal/UnsafeAllocator.java delete mode 100644 src/gson/main/java/com/google/gson/internal/bind/ArrayTypeAdapter.java delete mode 100644 src/gson/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java delete mode 100644 src/gson/main/java/com/google/gson/internal/bind/DateTypeAdapter.java delete mode 100644 src/gson/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java delete mode 100644 src/gson/main/java/com/google/gson/internal/bind/JsonTreeReader.java delete mode 100644 src/gson/main/java/com/google/gson/internal/bind/JsonTreeWriter.java delete mode 100644 src/gson/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java delete mode 100644 src/gson/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java delete mode 100644 src/gson/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java delete mode 100644 src/gson/main/java/com/google/gson/internal/bind/SqlDateTypeAdapter.java delete mode 100644 src/gson/main/java/com/google/gson/internal/bind/TimeTypeAdapter.java delete mode 100644 src/gson/main/java/com/google/gson/internal/bind/TreeTypeAdapter.java delete mode 100644 src/gson/main/java/com/google/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java delete mode 100644 src/gson/main/java/com/google/gson/internal/bind/TypeAdapters.java delete mode 100644 src/gson/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java delete mode 100644 src/gson/main/java/com/google/gson/internal/package-info.java delete mode 100644 src/gson/main/java/com/google/gson/package-info.java delete mode 100644 src/gson/main/java/com/google/gson/reflect/TypeToken.java delete mode 100644 src/gson/main/java/com/google/gson/reflect/package-info.java delete mode 100644 src/gson/main/java/com/google/gson/stream/JsonReader.java delete mode 100644 src/gson/main/java/com/google/gson/stream/JsonScope.java delete mode 100644 src/gson/main/java/com/google/gson/stream/JsonToken.java delete mode 100644 src/gson/main/java/com/google/gson/stream/JsonWriter.java delete mode 100644 src/gson/main/java/com/google/gson/stream/MalformedJsonException.java delete mode 100644 src/jsquish/main/java/gr/zdimensions/jsquish/ColourBlock.java delete mode 100644 src/jsquish/main/java/gr/zdimensions/jsquish/ColourSet.java delete mode 100644 src/jsquish/main/java/gr/zdimensions/jsquish/CompressorAlpha.java delete mode 100644 src/jsquish/main/java/gr/zdimensions/jsquish/CompressorCluster.java delete mode 100644 src/jsquish/main/java/gr/zdimensions/jsquish/CompressorColourFit.java delete mode 100644 src/jsquish/main/java/gr/zdimensions/jsquish/CompressorRange.java delete mode 100644 src/jsquish/main/java/gr/zdimensions/jsquish/CompressorSingleColour.java delete mode 100644 src/jsquish/main/java/gr/zdimensions/jsquish/Matrix.java delete mode 100644 src/jsquish/main/java/gr/zdimensions/jsquish/SingleColourLookup3.java delete mode 100644 src/jsquish/main/java/gr/zdimensions/jsquish/SingleColourLookup4.java delete mode 100644 src/jsquish/main/java/gr/zdimensions/jsquish/Squish.java delete mode 100644 src/jsquish/main/java/gr/zdimensions/jsquish/Vec.java delete mode 100644 src/lev/FastByteArrayInputStream.java delete mode 100644 src/lev/FastByteArrayOutputStream.java delete mode 100644 src/lev/LByteChannel.java delete mode 100644 src/lev/LByteSearcher.java delete mode 100644 src/lev/LExport.java delete mode 100644 src/lev/LFlags.java delete mode 100644 src/lev/LGlobal.java delete mode 100644 src/lev/LImport.java delete mode 100644 src/lev/LInChannel.java delete mode 100644 src/lev/LMergable.java delete mode 100644 src/lev/LMergeList.java delete mode 100644 src/lev/LMergeMap.java delete mode 100644 src/lev/LOutChannel.java delete mode 100644 src/lev/LOutFile.java delete mode 100644 src/lev/LPair.java delete mode 100644 src/lev/LShrinkArray.java delete mode 100644 src/lev/LStringSearcher.java delete mode 100644 src/lev/Ln.java delete mode 100644 src/lev/debug/LDebug.java delete mode 100644 src/lev/debug/LLogger.java delete mode 100644 src/lev/gui/JAutoComboBox.java delete mode 100644 src/lev/gui/JAutoTextField.java delete mode 100644 src/lev/gui/LAreaChart.java delete mode 100644 src/lev/gui/LButton.java delete mode 100644 src/lev/gui/LCenterPanel.java delete mode 100644 src/lev/gui/LChart.java delete mode 100644 src/lev/gui/LCheckBox.java delete mode 100644 src/lev/gui/LCheckBoxConfig.java delete mode 100644 src/lev/gui/LColorSetting.java delete mode 100644 src/lev/gui/LComboBox.java delete mode 100644 src/lev/gui/LComboSearchBox.java delete mode 100644 src/lev/gui/LComponent.java delete mode 100644 src/lev/gui/LDoubleSetting.java delete mode 100644 src/lev/gui/LDoubleSpinner.java delete mode 100644 src/lev/gui/LEditorPane.java delete mode 100644 src/lev/gui/LFileTree.java delete mode 100644 src/lev/gui/LFrame.java delete mode 100644 src/lev/gui/LHTMLPane.java delete mode 100644 src/lev/gui/LHelpComponent.java delete mode 100644 src/lev/gui/LHelpPanel.java delete mode 100644 src/lev/gui/LImagePane.java delete mode 100644 src/lev/gui/LLabel.java delete mode 100644 src/lev/gui/LList.java delete mode 100644 src/lev/gui/LMenuItem.java delete mode 100644 src/lev/gui/LNumericSetting.java delete mode 100644 src/lev/gui/LPanel.java delete mode 100644 src/lev/gui/LProgressBar.java delete mode 100644 src/lev/gui/LProgressBarFrame.java delete mode 100644 src/lev/gui/LProgressBarInterface.java delete mode 100644 src/lev/gui/LSaveFile.java delete mode 100644 src/lev/gui/LScrollPane.java delete mode 100644 src/lev/gui/LSlider.java delete mode 100644 src/lev/gui/LSpinner.java delete mode 100644 src/lev/gui/LSwingTreeNode.java delete mode 100644 src/lev/gui/LSwingWorker.java delete mode 100644 src/lev/gui/LTextArea.java delete mode 100644 src/lev/gui/LTextField.java delete mode 100644 src/lev/gui/LTextPane.java delete mode 100644 src/lev/gui/LTree.java delete mode 100644 src/lev/gui/LUserSetting.java delete mode 100644 src/lev/gui/Lg.java delete mode 100644 src/lev/gui/SaveBool.java delete mode 100644 src/lev/gui/SaveColor.java delete mode 100644 src/lev/gui/SaveDouble.java delete mode 100644 src/lev/gui/SaveEnum.java delete mode 100644 src/lev/gui/SaveFloat.java delete mode 100644 src/lev/gui/SaveInt.java delete mode 100644 src/lev/gui/SaveString.java delete mode 100644 src/lev/gui/SaveStringList.java delete mode 100644 src/lev/gui/Setting.java delete mode 100644 src/lev/gui/SortedListModel.java delete mode 100644 src/lev/gui/resources/ArrowLeft.png delete mode 100644 src/lev/gui/resources/ArrowLeftDark.png delete mode 100644 src/lev/gui/resources/ArrowRight.png delete mode 100644 src/lev/gui/resources/ArrowRightDark.png delete mode 100644 src/lev/gui/resources/LFonts.java delete mode 100644 src/lev/gui/resources/LImages.java delete mode 100644 src/lev/gui/resources/MyriadPro-Regular.ttf delete mode 100644 src/lev/gui/resources/NEUROPOL.ttf delete mode 100644 src/lev/gui/resources/OLEO.TTF delete mode 100644 src/lev/gui/resources/OptimusPrinceps.ttf delete mode 100644 src/lev/gui/resources/OptimusPrincepsSemiBold.ttf delete mode 100644 src/lev/gui/resources/Typo3-Medium.ttf delete mode 100644 src/lev/gui/resources/multipurpose.png delete mode 100644 src/lev/gui/resources/myriadwebpro-bold.ttf delete mode 100644 src/lev/gui/resources/reasonSystem-Regular.ttf delete mode 100644 src/lev/gui/resources/typo3Normal.ttf delete mode 100644 src/skyproc/ALCH.java delete mode 100644 src/skyproc/AMMO.java delete mode 100644 src/skyproc/ARMA.java delete mode 100644 src/skyproc/ARMO.java delete mode 100644 src/skyproc/AVIF.java delete mode 100644 src/skyproc/AltTextures.java delete mode 100644 src/skyproc/BOOK.java delete mode 100644 src/skyproc/BSA.java delete mode 100644 src/skyproc/BodyTemplate.java delete mode 100644 src/skyproc/COBJ.java delete mode 100644 src/skyproc/CONT.java delete mode 100644 src/skyproc/Condition.java delete mode 100644 src/skyproc/ConditionBase.java delete mode 100644 src/skyproc/ConditionOption.java delete mode 100644 src/skyproc/Consistency.java delete mode 100644 src/skyproc/DIAL.java delete mode 100644 src/skyproc/DLBR.java delete mode 100644 src/skyproc/DLVW.java delete mode 100644 src/skyproc/DestructionData.java delete mode 100644 src/skyproc/ECZN.java delete mode 100644 src/skyproc/ENCH.java delete mode 100644 src/skyproc/EmbeddedScripts.java delete mode 100644 src/skyproc/FACT.java delete mode 100644 src/skyproc/FLST.java delete mode 100644 src/skyproc/FormID.java delete mode 100644 src/skyproc/GLOB.java delete mode 100644 src/skyproc/GMST.java delete mode 100644 src/skyproc/GRUP.java delete mode 100644 src/skyproc/GRUPRecursive.java delete mode 100644 src/skyproc/GRUP_TYPE.java delete mode 100644 src/skyproc/HDPT.java delete mode 100644 src/skyproc/IMGS.java delete mode 100644 src/skyproc/INFO.java delete mode 100644 src/skyproc/INGR.java delete mode 100644 src/skyproc/ItemListing.java delete mode 100644 src/skyproc/KYWD.java delete mode 100644 src/skyproc/KeywordSet.java delete mode 100644 src/skyproc/LGTM.java delete mode 100644 src/skyproc/LIGH.java delete mode 100644 src/skyproc/LVLI.java delete mode 100644 src/skyproc/LVLN.java delete mode 100644 src/skyproc/LeveledEntry.java delete mode 100644 src/skyproc/LeveledRecord.java delete mode 100644 src/skyproc/MGEF.java delete mode 100644 src/skyproc/MISC.java delete mode 100644 src/skyproc/MagicEffectRef.java delete mode 100644 src/skyproc/MagicItem.java delete mode 100644 src/skyproc/MajorRecord.java delete mode 100644 src/skyproc/MajorRecordDescription.java delete mode 100644 src/skyproc/MajorRecordNamed.java delete mode 100644 src/skyproc/Mod.java delete mode 100644 src/skyproc/ModExporter.java delete mode 100644 src/skyproc/ModListing.java delete mode 100644 src/skyproc/Model.java delete mode 100644 src/skyproc/NIF.java delete mode 100644 src/skyproc/NPC_.java delete mode 100644 src/skyproc/NiftyFunc.java delete mode 100644 src/skyproc/OTFT.java delete mode 100644 src/skyproc/Owner.java delete mode 100644 src/skyproc/PERK.java delete mode 100644 src/skyproc/PROJ.java delete mode 100644 src/skyproc/QUST.java delete mode 100644 src/skyproc/RACE.java delete mode 100644 src/skyproc/RGB.java delete mode 100644 src/skyproc/RGBA.java delete mode 100644 src/skyproc/Record.java delete mode 100644 src/skyproc/RecordFileChannel.java delete mode 100644 src/skyproc/RecordShrinkArray.java delete mode 100644 src/skyproc/SCRL.java delete mode 100644 src/skyproc/SPDatabase.java delete mode 100644 src/skyproc/SPEL.java delete mode 100644 src/skyproc/SPGlobal.java delete mode 100644 src/skyproc/SPImporter.java delete mode 100644 src/skyproc/SPLogger.java delete mode 100644 src/skyproc/STAT.java delete mode 100644 src/skyproc/SUMMergerProgram.java delete mode 100644 src/skyproc/ScriptPackage.java delete mode 100644 src/skyproc/ScriptProperty.java delete mode 100644 src/skyproc/ScriptRef.java delete mode 100644 src/skyproc/SkyProcSave.java delete mode 100644 src/skyproc/SkyProcTester.java delete mode 100644 src/skyproc/StringNonNull.java delete mode 100644 src/skyproc/StringNull.java delete mode 100644 src/skyproc/SubData.java delete mode 100644 src/skyproc/SubFlag.java delete mode 100644 src/skyproc/SubFloat.java delete mode 100644 src/skyproc/SubForm.java delete mode 100644 src/skyproc/SubFormArray.java delete mode 100644 src/skyproc/SubFormData.java delete mode 100644 src/skyproc/SubFormInt.java delete mode 100644 src/skyproc/SubInt.java delete mode 100644 src/skyproc/SubIntSigned.java delete mode 100644 src/skyproc/SubList.java delete mode 100644 src/skyproc/SubListCounted.java delete mode 100644 src/skyproc/SubListMulti.java delete mode 100644 src/skyproc/SubListSorted.java delete mode 100644 src/skyproc/SubMarkerSet.java delete mode 100644 src/skyproc/SubPrototype.java delete mode 100644 src/skyproc/SubRGB.java delete mode 100644 src/skyproc/SubRGBshort.java delete mode 100644 src/skyproc/SubRecord.java delete mode 100644 src/skyproc/SubRecordTyped.java delete mode 100644 src/skyproc/SubRecords.java delete mode 100644 src/skyproc/SubRecordsCopied.java delete mode 100644 src/skyproc/SubRecordsDerived.java delete mode 100644 src/skyproc/SubRecordsStream.java delete mode 100644 src/skyproc/SubShell.java delete mode 100644 src/skyproc/SubShellBulkNumber.java delete mode 100644 src/skyproc/SubShellBulkType.java delete mode 100644 src/skyproc/SubString.java delete mode 100644 src/skyproc/SubStringNonNull.java delete mode 100644 src/skyproc/SubStringPointer.java delete mode 100644 src/skyproc/TXST.java delete mode 100644 src/skyproc/VTYP.java delete mode 100644 src/skyproc/WEAP.java delete mode 100644 src/skyproc/WTHR.java delete mode 100644 src/skyproc/exceptions/BadMod.java delete mode 100644 src/skyproc/exceptions/BadParameter.java delete mode 100644 src/skyproc/exceptions/BadRecord.java delete mode 100644 src/skyproc/exceptions/MissingMaster.java delete mode 100644 src/skyproc/exceptions/NotFound.java delete mode 100644 src/skyproc/exceptions/Uninitialized.java delete mode 100644 src/skyproc/genenums/ActorValue.java delete mode 100644 src/skyproc/genenums/ArmorType.java delete mode 100644 src/skyproc/genenums/Axis.java delete mode 100644 src/skyproc/genenums/CastType.java delete mode 100644 src/skyproc/genenums/CastingSource.java delete mode 100644 src/skyproc/genenums/CrimeType.java delete mode 100644 src/skyproc/genenums/DeliveryType.java delete mode 100644 src/skyproc/genenums/EmotionType.java delete mode 100644 src/skyproc/genenums/FavorLevel.java delete mode 100644 src/skyproc/genenums/FirstPersonFlags.java delete mode 100644 src/skyproc/genenums/Gender.java delete mode 100644 src/skyproc/genenums/HeadPartFlags.java delete mode 100644 src/skyproc/genenums/MovementType.java delete mode 100644 src/skyproc/genenums/Perspective.java delete mode 100644 src/skyproc/genenums/Skill.java delete mode 100644 src/skyproc/genenums/SoundVolume.java delete mode 100644 src/skyproc/genenums/WardState.java delete mode 100644 src/skyproc/gui/BackToSUM.png delete mode 100644 src/skyproc/gui/BackToSUMdark.png delete mode 100644 src/skyproc/gui/LFormIDPicker.java delete mode 100644 src/skyproc/gui/Open Settings Collapsed.png delete mode 100644 src/skyproc/gui/Open Settings.png delete mode 100644 src/skyproc/gui/SPDefaultGUI.java delete mode 100644 src/skyproc/gui/SPList.java delete mode 100644 src/skyproc/gui/SPMainMenuConfig.java delete mode 100644 src/skyproc/gui/SPMainMenuPanel.java delete mode 100644 src/skyproc/gui/SPProgressBarFrame.java delete mode 100644 src/skyproc/gui/SPProgressBarPlug.java delete mode 100644 src/skyproc/gui/SPQuestionPanel.java delete mode 100644 src/skyproc/gui/SPSettingDefaultsPanel.java delete mode 100644 src/skyproc/gui/SPSettingPanel.java delete mode 100644 src/skyproc/gui/SPStringList.java delete mode 100644 src/skyproc/gui/SUM program.png delete mode 100644 src/skyproc/gui/SUM.java delete mode 100644 src/skyproc/gui/SUMGUI.java delete mode 100644 src/skyproc/gui/SUMprogram.java delete mode 100644 src/skyproc/gui/SaveFormList.java delete mode 100644 src/skyproc/gui/SkyProc Logo Small.png delete mode 100644 src/skyproc/gui/background.jpg diff --git a/.gitignore b/.gitignore index b413404..b2c9b4c 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,7 @@ .mtj.tmp/ # Package Files # -*.jar +#*.jar *.war *.ear *.zip diff --git a/build.xml b/build.xml index 49cf66e..ec1ba97 100644 --- a/build.xml +++ b/build.xml @@ -41,4 +41,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/DDSUtils.jar b/lib/DDSUtils.jar new file mode 100644 index 0000000000000000000000000000000000000000..a54f4a94fc80f28df39fe2457091d44635499f72 GIT binary patch literal 213600 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�ELw3w4e7K3UDp3cDKk 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

4Wgd;A5LbYih$HX|iELwhFsL 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

GaRD2x2%`))8nI@@Nv;K{^K7? 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 literal 0 HcmV?d00001 diff --git a/lib/gson-2.8.1.jar b/lib/gson-2.8.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..5222355ad07b3d6565185fcb00fd1e9b57a1fdbc GIT binary patch literal 232620 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 z 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 Kn|WL*&HAFA3I?%|$L6ZJ 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

T6~bR6_)-Ix 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 literal 0 HcmV?d00001 diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml index 5627b61..e5f0887 100644 --- a/nbproject/build-impl.xml +++ b/nbproject/build-impl.xml @@ -903,6 +903,22 @@ is divided into following sections: + + + + + + + + + + + + + + + + @@ -1388,6 +1404,22 @@ is divided into following sections: + + + + + + + + + + + + + + + + diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties index 2a0ccd9..d41c81e 100644 --- a/nbproject/genfiles.properties +++ b/nbproject/genfiles.properties @@ -3,6 +3,6 @@ build.xml.script.CRC32=4603f28b build.xml.stylesheet.CRC32=28e38971@1.44.1.45 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. -nbproject/build-impl.xml.data.CRC32=2f42a6e5 -nbproject/build-impl.xml.script.CRC32=3fc9ecfa +nbproject/build-impl.xml.data.CRC32=95f6d633 +nbproject/build-impl.xml.script.CRC32=94c384e0 nbproject/build-impl.xml.stylesheet.CRC32=830a3534@1.80.1.48 diff --git a/nbproject/project.properties b/nbproject/project.properties index 1e51c02..cc49713 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -27,21 +27,17 @@ dist.jar=${dist.dir}/AutomaticVariants.jar dist.javadoc.dir=${dist.dir}/javadoc endorsed.classpath= excludes= -file.reference.ini4j-0.5.4.jar=lib/ini4j-0.5.4.jar -file.reference.jcommon-1.0.23.jar=lib/jcommon-1.0.23.jar -file.reference.jcommon-xml-1.0.23.jar=lib/jcommon-xml-1.0.23.jar -file.reference.jfreechart-1.0.19.jar=lib/jfreechart-1.0.19.jar -file.reference.WinRegistry.jar=lib/WinRegistry.jar +file.reference.DDSUtils.jar=lib/DDSUtils.jar +file.reference.gson-2.8.1.jar=lib/gson-2.8.1.jar includes=** jar.archive.disabled=${jnlp.enabled} jar.compress=false jar.index=${jnlp.enabled} javac.classpath=\ - ${file.reference.ini4j-0.5.4.jar}:\ - ${file.reference.jcommon-1.0.23.jar}:\ - ${file.reference.jcommon-xml-1.0.23.jar}:\ - ${file.reference.jfreechart-1.0.19.jar}:\ - ${file.reference.WinRegistry.jar} + ${reference.lev.jar}:\ + ${reference.skyproc.jar}:\ + ${file.reference.gson-2.8.1.jar}:\ + ${file.reference.DDSUtils.jar} # Space-separated list of extra javac options javac.compilerargs= javac.deprecation=false @@ -80,6 +76,10 @@ manifest.file=manifest.mf meta.inf.dir=${src.dir}/META-INF mkdist.disabled=false platform.active=default_platform +project.lev=../lev/Lev Library Project +project.skyproc=../skyproc +reference.lev.jar=${project.lev}/dist/lev.jar +reference.skyproc.jar=${project.skyproc}/dist/skyproc.jar run.classpath=\ ${javac.classpath}:\ ${build.classes.dir} diff --git a/nbproject/project.xml b/nbproject/project.xml index d3dd2d2..501b2f7 100644 --- a/nbproject/project.xml +++ b/nbproject/project.xml @@ -11,5 +11,23 @@ + + + lev + jar + + jar + clean + jar + + + skyproc + jar + + jar + clean + jar + + diff --git a/src/aircompressor/main/java/io/airlift/compress/Compressor.java b/src/aircompressor/main/java/io/airlift/compress/Compressor.java deleted file mode 100644 index 9719dba..0000000 --- a/src/aircompressor/main/java/io/airlift/compress/Compressor.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.airlift.compress; - -import java.nio.ByteBuffer; - -public interface Compressor -{ - int maxCompressedLength(int uncompressedSize); - - /** - * @return number of bytes written to the output - */ - int compress(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int maxOutputLength); - - void compress(ByteBuffer input, ByteBuffer output); -} diff --git a/src/aircompressor/main/java/io/airlift/compress/Decompressor.java b/src/aircompressor/main/java/io/airlift/compress/Decompressor.java deleted file mode 100644 index 6cf290c..0000000 --- a/src/aircompressor/main/java/io/airlift/compress/Decompressor.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.airlift.compress; - -import java.nio.ByteBuffer; - -public interface Decompressor -{ - /** - * @return number of bytes written to the output - */ - int decompress(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int maxOutputLength) - throws MalformedInputException; - - void decompress(ByteBuffer input, ByteBuffer output) - throws MalformedInputException; -} diff --git a/src/aircompressor/main/java/io/airlift/compress/MalformedInputException.java b/src/aircompressor/main/java/io/airlift/compress/MalformedInputException.java deleted file mode 100644 index eb487a3..0000000 --- a/src/aircompressor/main/java/io/airlift/compress/MalformedInputException.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.airlift.compress; - -public class MalformedInputException - extends RuntimeException -{ - private final long offset; - - public MalformedInputException(long offset) - { - this(offset, "Malformed input"); - } - - public MalformedInputException(long offset, String reason) - { - super(reason + ": offset=" + offset); - this.offset = offset; - } - - public long getOffset() - { - return offset; - } -} diff --git a/src/aircompressor/main/java/io/airlift/compress/lz4/Lz4Compressor.java b/src/aircompressor/main/java/io/airlift/compress/lz4/Lz4Compressor.java deleted file mode 100644 index 6e87b66..0000000 --- a/src/aircompressor/main/java/io/airlift/compress/lz4/Lz4Compressor.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.airlift.compress.lz4; - -import io.airlift.compress.Compressor; - -import java.nio.ByteBuffer; - -import static io.airlift.compress.lz4.Lz4RawCompressor.MAX_TABLE_SIZE; -import static io.airlift.compress.lz4.UnsafeUtil.getAddress; -import static sun.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; - -/** - * This class is not thread-safe - */ -public class Lz4Compressor - implements Compressor -{ - private final int[] table = new int[MAX_TABLE_SIZE]; - - @Override - public int maxCompressedLength(int uncompressedSize) - { - return Lz4RawCompressor.maxCompressedLength(uncompressedSize); - } - - @Override - public int compress(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int maxOutputLength) - { - long inputAddress = ARRAY_BYTE_BASE_OFFSET + inputOffset; - long outputAddress = ARRAY_BYTE_BASE_OFFSET + outputOffset; - - return Lz4RawCompressor.compress(input, inputAddress, inputLength, output, outputAddress, maxOutputLength, table); - } - - @Override - public void compress(ByteBuffer input, ByteBuffer output) - { - Object inputBase; - long inputAddress; - long inputLimit; - if (input.isDirect()) { - inputBase = null; - long address = getAddress(input); - inputAddress = address + input.position(); - inputLimit = address + input.limit(); - } - else if (input.hasArray()) { - inputBase = input.array(); - inputAddress = ARRAY_BYTE_BASE_OFFSET + input.arrayOffset() + input.position(); - inputLimit = ARRAY_BYTE_BASE_OFFSET + input.arrayOffset() + input.limit(); - } - else { - throw new IllegalArgumentException("Unsupported input ByteBuffer implementation " + input.getClass().getName()); - } - - Object outputBase; - long outputAddress; - long outputLimit; - if (output.isDirect()) { - outputBase = null; - long address = getAddress(output); - outputAddress = address + output.position(); - outputLimit = address + output.limit(); - } - else if (output.hasArray()) { - outputBase = output.array(); - outputAddress = ARRAY_BYTE_BASE_OFFSET + output.arrayOffset() + output.position(); - outputLimit = ARRAY_BYTE_BASE_OFFSET + output.arrayOffset() + output.limit(); - } - else { - throw new IllegalArgumentException("Unsupported output ByteBuffer implementation " + output.getClass().getName()); - } - - // HACK: Assure JVM does not collect Slice wrappers while compressing, since the - // collection may trigger freeing of the underlying memory resulting in a segfault - // There is no other known way to signal to the JVM that an object should not be - // collected in a block, and technically, the JVM is allowed to eliminate these locks. - synchronized (input) { - synchronized (output) { - int written = Lz4RawCompressor.compress( - inputBase, - inputAddress, - (int) (inputLimit - inputAddress), - outputBase, - outputAddress, - outputLimit - outputAddress, - table); - output.position(output.position() + written); - } - } - } -} diff --git a/src/aircompressor/main/java/io/airlift/compress/lz4/Lz4Constants.java b/src/aircompressor/main/java/io/airlift/compress/lz4/Lz4Constants.java deleted file mode 100644 index 9182e66..0000000 --- a/src/aircompressor/main/java/io/airlift/compress/lz4/Lz4Constants.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.airlift.compress.lz4; - -final class Lz4Constants -{ - public static final int LAST_LITERAL_SIZE = 5; - public static final int MIN_MATCH = 4; - - public static final int SIZE_OF_SHORT = 2; - public static final int SIZE_OF_INT = 4; - public static final int SIZE_OF_LONG = 8; - - private Lz4Constants() {} -} diff --git a/src/aircompressor/main/java/io/airlift/compress/lz4/Lz4Decompressor.java b/src/aircompressor/main/java/io/airlift/compress/lz4/Lz4Decompressor.java deleted file mode 100644 index 8e81592..0000000 --- a/src/aircompressor/main/java/io/airlift/compress/lz4/Lz4Decompressor.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.airlift.compress.lz4; - -import io.airlift.compress.Decompressor; -import io.airlift.compress.MalformedInputException; - -import java.nio.ByteBuffer; - -import static io.airlift.compress.lz4.UnsafeUtil.getAddress; -import static sun.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; - -public class Lz4Decompressor - implements Decompressor -{ - @Override - public int decompress(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int maxOutputLength) - throws MalformedInputException - { - long inputAddress = ARRAY_BYTE_BASE_OFFSET + inputOffset; - long inputLimit = inputAddress + inputLength; - long outputAddress = ARRAY_BYTE_BASE_OFFSET + outputOffset; - long outputLimit = outputAddress + maxOutputLength; - - return Lz4RawDecompressor.decompress(input, inputAddress, inputLimit, output, outputAddress, outputLimit); - } - - @Override - public void decompress(ByteBuffer input, ByteBuffer output) - throws MalformedInputException - { - Object inputBase; - long inputAddress; - long inputLimit; - if (input.isDirect()) { - inputBase = null; - long address = getAddress(input); - inputAddress = address + input.position(); - inputLimit = address + input.limit(); - } - else if (input.hasArray()) { - inputBase = input.array(); - inputAddress = ARRAY_BYTE_BASE_OFFSET + input.arrayOffset() + input.position(); - inputLimit = ARRAY_BYTE_BASE_OFFSET + input.arrayOffset() + input.limit(); - } - else { - throw new IllegalArgumentException("Unsupported input ByteBuffer implementation " + input.getClass().getName()); - } - - Object outputBase; - long outputAddress; - long outputLimit; - if (output.isDirect()) { - outputBase = null; - long address = getAddress(output); - outputAddress = address + output.position(); - outputLimit = address + output.limit(); - } - else if (output.hasArray()) { - outputBase = output.array(); - outputAddress = ARRAY_BYTE_BASE_OFFSET + output.arrayOffset() + output.position(); - outputLimit = ARRAY_BYTE_BASE_OFFSET + output.arrayOffset() + output.limit(); - } - else { - throw new IllegalArgumentException("Unsupported output ByteBuffer implementation " + output.getClass().getName()); - } - - // HACK: Assure JVM does not collect Slice wrappers while decompressing, since the - // collection may trigger freeing of the underlying memory resulting in a segfault - // There is no other known way to signal to the JVM that an object should not be - // collected in a block, and technically, the JVM is allowed to eliminate these locks. - synchronized (input) { - synchronized (output) { - int written = Lz4RawDecompressor.decompress(inputBase, inputAddress, inputLimit, outputBase, outputAddress, outputLimit); - output.position(output.position() + written); - } - } - } -} diff --git a/src/aircompressor/main/java/io/airlift/compress/lz4/Lz4RawCompressor.java b/src/aircompressor/main/java/io/airlift/compress/lz4/Lz4RawCompressor.java deleted file mode 100644 index b687d4d..0000000 --- a/src/aircompressor/main/java/io/airlift/compress/lz4/Lz4RawCompressor.java +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.airlift.compress.lz4; - -import java.util.Arrays; - -import static io.airlift.compress.lz4.Lz4Constants.LAST_LITERAL_SIZE; -import static io.airlift.compress.lz4.Lz4Constants.MIN_MATCH; -import static io.airlift.compress.lz4.Lz4Constants.SIZE_OF_INT; -import static io.airlift.compress.lz4.Lz4Constants.SIZE_OF_LONG; -import static io.airlift.compress.lz4.Lz4Constants.SIZE_OF_SHORT; -import static io.airlift.compress.lz4.UnsafeUtil.UNSAFE; - -public final class Lz4RawCompressor -{ - private static final int MAX_INPUT_SIZE = 0x7E000000; /* 2 113 929 216 bytes */ - - private static final int HASH_LOG = 12; - - private static final int MIN_TABLE_SIZE = 16; - public static final int MAX_TABLE_SIZE = (1 << HASH_LOG); - - private static final int COPY_LENGTH = 8; - private static final int MATCH_FIND_LIMIT = COPY_LENGTH + MIN_MATCH; - - private static final int MIN_LENGTH = MATCH_FIND_LIMIT + 1; - - private static final int ML_BITS = 4; - private static final int ML_MASK = (1 << ML_BITS) - 1; - private static final int RUN_BITS = 8 - ML_BITS; - private static final int RUN_MASK = (1 << RUN_BITS) - 1; - - private static final int MAX_DISTANCE = ((1 << 16) - 1); - - private static final int SKIP_TRIGGER = 6; /* Increase this value ==> compression run slower on incompressible data */ - - private Lz4RawCompressor() {} - - private static int hash(long value, int mask) - { - // Multiplicative hash. It performs the equivalent to - // this computation: - // - // value * frac(a) - // - // for some real number 'a' with a good & random mix - // of 1s and 0s in its binary representation - // - // For performance, it does it using fixed point math - return (int) ((value * 889523592379L >>> 28) & mask); - } - - public static int maxCompressedLength(int sourceLength) - { - return sourceLength + sourceLength / 255 + 16; - } - - public static int compress( - final Object inputBase, - final long inputAddress, - final int inputLength, - final Object outputBase, - final long outputAddress, - final long maxOutputLength, - final int[] table) - { - int tableSize = computeTableSize(inputLength); - Arrays.fill(table, 0, tableSize, 0); - - int mask = tableSize - 1; - - if (inputLength > MAX_INPUT_SIZE) { - throw new IllegalArgumentException("Max input length exceeded"); - } - - if (maxOutputLength < maxCompressedLength(inputLength)) { - throw new IllegalArgumentException("Max output length must be larger than " + maxCompressedLength(inputLength)); - } - - long input = inputAddress; - long output = outputAddress; - - final long inputLimit = inputAddress + inputLength; - final long matchFindLimit = inputLimit - MATCH_FIND_LIMIT; - final long matchLimit = inputLimit - LAST_LITERAL_SIZE; - - if (inputLength < MIN_LENGTH) { - output = emitLastLiteral(outputBase, output, inputBase, input, inputLimit - input); - return (int) (output - outputAddress); - } - - long anchor = input; - - // First Byte - // put position in hash - table[hash(UNSAFE.getLong(inputBase, input), mask)] = (int) (input - inputAddress); - - input++; - int nextHash = hash(UNSAFE.getLong(inputBase, input), mask); - - boolean done = false; - do { - long nextInputIndex = input; - int findMatchAttempts = 1 << SKIP_TRIGGER; - int step = 1; - - // find 4-byte match - long matchIndex; - do { - int hash = nextHash; - input = nextInputIndex; - nextInputIndex += step; - - step = (findMatchAttempts++) >>> SKIP_TRIGGER; - - if (nextInputIndex > matchFindLimit) { - return (int) (emitLastLiteral(outputBase, output, inputBase, anchor, inputLimit - anchor) - outputAddress); - } - - // get position on hash - matchIndex = inputAddress + table[hash]; - nextHash = hash(UNSAFE.getLong(inputBase, nextInputIndex), mask); - - // put position on hash - table[hash] = (int) (input - inputAddress); - } - while (UNSAFE.getInt(inputBase, matchIndex) != UNSAFE.getInt(inputBase, input) || matchIndex + MAX_DISTANCE < input); - - // catch up - while ((input > anchor) && (matchIndex > inputAddress) && (UNSAFE.getByte(inputBase, input - 1) == UNSAFE.getByte(inputBase, matchIndex - 1))) { - --input; - --matchIndex; - } - - int literalLength = (int) (input - anchor); - long tokenAddress = output; - - output = emitLiteral(inputBase, outputBase, anchor, literalLength, tokenAddress); - - // next match - while (true) { - // find match length - int matchLength = count(inputBase, input + MIN_MATCH, matchIndex + MIN_MATCH, matchLimit); - output = emitMatch(outputBase, output, tokenAddress, (short) (input - matchIndex), matchLength); - - input += matchLength + MIN_MATCH; - - anchor = input; - - // are we done? - if (input > matchFindLimit) { - done = true; - break; - } - - long position = input - 2; - table[hash(UNSAFE.getLong(inputBase, position), mask)] = (int) (position - inputAddress); - - // Test next position - int hash = hash(UNSAFE.getLong(inputBase, input), mask); - matchIndex = inputAddress + table[hash]; - table[hash] = (int) (input - inputAddress); - - if (matchIndex + MAX_DISTANCE < input || UNSAFE.getInt(inputBase, matchIndex) != UNSAFE.getInt(inputBase, input)) { - input++; - nextHash = hash(UNSAFE.getLong(inputBase, input), mask); - break; - } - - // go for another match - tokenAddress = output++; - UNSAFE.putByte(outputBase, tokenAddress, (byte) 0); - } - } - while (!done); - - // Encode Last Literals - output = emitLastLiteral(outputBase, output, inputBase, anchor, inputLimit - anchor); - - return (int) (output - outputAddress); - } - - private static long emitLiteral(Object inputBase, Object outputBase, long input, int literalLength, long output) - { - output = encodeRunLength(outputBase, output, literalLength); - - final long outputLimit = output + literalLength; - do { - UNSAFE.putLong(outputBase, output, UNSAFE.getLong(inputBase, input)); - input += SIZE_OF_LONG; - output += SIZE_OF_LONG; - } - while (output < outputLimit); - - return outputLimit; - } - - private static long emitMatch(Object outputBase, long output, long tokenAddress, short offset, long matchLength) - { - // write offset - UNSAFE.putShort(outputBase, output, offset); - output += SIZE_OF_SHORT; - - // write match length - if (matchLength >= ML_MASK) { - UNSAFE.putByte(outputBase, tokenAddress, (byte) (UNSAFE.getByte(outputBase, tokenAddress) | ML_MASK)); - long remaining = matchLength - ML_MASK; - while (remaining >= 510) { - UNSAFE.putShort(outputBase, output, (short) 0xFFFF); - output += SIZE_OF_SHORT; - remaining -= 510; - } - if (remaining >= 255) { - UNSAFE.putByte(outputBase, output++, (byte) 255); - remaining -= 255; - } - UNSAFE.putByte(outputBase, output++, (byte) remaining); - } - else { - UNSAFE.putByte(outputBase, tokenAddress, (byte) (UNSAFE.getByte(outputBase, tokenAddress) | matchLength)); - } - - return output; - } - - private static int count(Object inputBase, final long start, long matchStart, long matchLimit) - { - long current = start; - - // first, compare long at a time - while (current < matchLimit - (SIZE_OF_LONG - 1)) { - long diff = UNSAFE.getLong(inputBase, matchStart) ^ UNSAFE.getLong(inputBase, current); - if (diff != 0) { - current += Long.numberOfTrailingZeros(diff) >> 3; - return (int) (current - start); - } - - current += SIZE_OF_LONG; - matchStart += SIZE_OF_LONG; - } - - if (current < matchLimit - (SIZE_OF_INT - 1) && UNSAFE.getInt(inputBase, matchStart) == UNSAFE.getInt(inputBase, current)) { - current += SIZE_OF_INT; - matchStart += SIZE_OF_INT; - } - - if (current < matchLimit - (SIZE_OF_SHORT - 1) && UNSAFE.getShort(inputBase, matchStart) == UNSAFE.getShort(inputBase, current)) { - current += SIZE_OF_SHORT; - matchStart += SIZE_OF_SHORT; - } - - if (current < matchLimit && UNSAFE.getByte(inputBase, matchStart) == UNSAFE.getByte(inputBase, current)) { - ++current; - } - - return (int) (current - start); - } - - private static long emitLastLiteral( - final Object outputBase, - final long outputAddress, - final Object inputBase, - final long inputAddress, - final long length) - { - long output = encodeRunLength(outputBase, outputAddress, length); - UNSAFE.copyMemory(inputBase, inputAddress, outputBase, output, length); - - return output + length; - } - - private static long encodeRunLength( - final Object base, - long output, - final long length) - { - if (length >= RUN_MASK) { - UNSAFE.putByte(base, output++, (byte) (RUN_MASK << ML_BITS)); - - long remaining = length - RUN_MASK; - while (remaining >= 255) { - UNSAFE.putByte(base, output++, (byte) 255); - remaining -= 255; - } - UNSAFE.putByte(base, output++, (byte) remaining); - } - else { - UNSAFE.putByte(base, output++, (byte) (length << ML_BITS)); - } - - return output; - } - - private static int computeTableSize(int inputSize) - { - // smallest power of 2 larger than inputSize - int target = Integer.highestOneBit(inputSize - 1) << 1; - - // keep it between MIN_TABLE_SIZE and MAX_TABLE_SIZE - return Math.max(Math.min(target, MAX_TABLE_SIZE), MIN_TABLE_SIZE); - } -} diff --git a/src/aircompressor/main/java/io/airlift/compress/lz4/Lz4RawDecompressor.java b/src/aircompressor/main/java/io/airlift/compress/lz4/Lz4RawDecompressor.java deleted file mode 100644 index 23398d1..0000000 --- a/src/aircompressor/main/java/io/airlift/compress/lz4/Lz4RawDecompressor.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.airlift.compress.lz4; - -import io.airlift.compress.MalformedInputException; - -import static io.airlift.compress.lz4.Lz4Constants.LAST_LITERAL_SIZE; -import static io.airlift.compress.lz4.Lz4Constants.MIN_MATCH; -import static io.airlift.compress.lz4.Lz4Constants.SIZE_OF_INT; -import static io.airlift.compress.lz4.Lz4Constants.SIZE_OF_LONG; -import static io.airlift.compress.lz4.Lz4Constants.SIZE_OF_SHORT; -import static io.airlift.compress.lz4.UnsafeUtil.UNSAFE; - -public final class Lz4RawDecompressor -{ - private static final int[] DEC_32_TABLE = {4, 1, 2, 1, 4, 4, 4, 4}; - private static final int[] DEC_64_TABLE = {0, 0, 0, -1, 0, 1, 2, 3}; - - private static final int OFFSET_SIZE = 2; - private static final int TOKEN_SIZE = 1; - - private Lz4RawDecompressor() {} - - public static int decompress( - final Object inputBase, - final long inputAddress, - final long inputLimit, - final Object outputBase, - final long outputAddress, - final long outputLimit) - { - final long fastOutputLimit = outputLimit - SIZE_OF_LONG; // maximum offset in output buffer to which it's safe to write long-at-a-time - - long input = inputAddress; - long output = outputAddress; - - if (inputAddress == inputLimit) { - throw new MalformedInputException(0, "input is empty"); - } - - if (outputAddress == outputLimit) { - if (inputLimit - inputAddress == 1 && UNSAFE.getByte(inputBase, inputAddress) == 0) { - return 0; - } - return -1; - } - - while (input < inputLimit) { - final int token = UNSAFE.getByte(inputBase, input++) & 0xFF; - - // decode literal length - int literalLength = token >>> 4; // top-most 4 bits of token - if (literalLength == 0xF) { - int value; - do { - value = UNSAFE.getByte(inputBase, input++) & 0xFF; - literalLength += value; - } - while (value == 255 && input < inputLimit - 15); - } - - // copy literal - long literalOutputLimit = output + literalLength; - if (literalOutputLimit > (fastOutputLimit - MIN_MATCH) || input + literalLength > inputLimit - (OFFSET_SIZE + TOKEN_SIZE + LAST_LITERAL_SIZE)) { - // copy the last literal and finish - if (literalOutputLimit > outputLimit) { - throw new MalformedInputException(input - inputAddress, "attempt to write last literal outside of destination buffer"); - } - - if (input + literalLength != inputLimit) { - throw new MalformedInputException(input - inputAddress, "all input must be consumed"); - } - - // slow, precise copy - UNSAFE.copyMemory(inputBase, input, outputBase, output, literalLength); - input += literalLength; - output += literalLength; - break; - } - - // fast copy. We may overcopy but there's enough room in input and output to not overrun them - do { - UNSAFE.putLong(outputBase, output, UNSAFE.getLong(inputBase, input)); - input += SIZE_OF_LONG; - output += SIZE_OF_LONG; - } - while (output < literalOutputLimit); - input -= (output - literalOutputLimit); // adjust index if we overcopied - output = literalOutputLimit; - - // get offset - // we know we can read two bytes because of the bounds check performed before copying the literal above - int offset = UNSAFE.getShort(inputBase, input) & 0xFFFF; - input += SIZE_OF_SHORT; - - long matchAddress = output - offset; - if (matchAddress < outputAddress) { - throw new MalformedInputException(input - inputAddress, "offset outside destination buffer"); - } - - // compute match length - int matchLength = token & 0xF; // bottom-most 4 bits of token - if (matchLength == 0xF) { - int value; - do { - if (input > inputLimit - LAST_LITERAL_SIZE) { - throw new MalformedInputException(input - inputAddress); - } - - value = UNSAFE.getByte(inputBase, input++) & 0xFF; - matchLength += value; - } - while (value == 255); - } - matchLength += MIN_MATCH; // implicit length from initial 4-byte match in encoder - - long matchOutputLimit = output + matchLength; - - // at this point we have at least 12 bytes of space in the output buffer - // due to the fastLimit check before copying a literal, so no need to check again - - // copy repeated sequence - if (offset < SIZE_OF_LONG) { - // 8 bytes apart so that we can copy long-at-a-time below - int increment32 = DEC_32_TABLE[offset]; - int decrement64 = DEC_64_TABLE[offset]; - - UNSAFE.putByte(outputBase, output, UNSAFE.getByte(outputBase, matchAddress)); - UNSAFE.putByte(outputBase, output + 1, UNSAFE.getByte(outputBase, matchAddress + 1)); - UNSAFE.putByte(outputBase, output + 2, UNSAFE.getByte(outputBase, matchAddress + 2)); - UNSAFE.putByte(outputBase, output + 3, UNSAFE.getByte(outputBase, matchAddress + 3)); - output += SIZE_OF_INT; - matchAddress += increment32; - - UNSAFE.putInt(outputBase, output, UNSAFE.getInt(outputBase, matchAddress)); - output += SIZE_OF_INT; - matchAddress -= decrement64; - } - else { - UNSAFE.putLong(outputBase, output, UNSAFE.getLong(outputBase, matchAddress)); - matchAddress += SIZE_OF_LONG; - output += SIZE_OF_LONG; - } - - if (matchOutputLimit > fastOutputLimit - MIN_MATCH) { - if (matchOutputLimit > outputLimit - LAST_LITERAL_SIZE) { - throw new MalformedInputException(input - inputAddress, String.format("last %s bytes must be literals", LAST_LITERAL_SIZE)); - } - - while (output < fastOutputLimit) { - UNSAFE.putLong(outputBase, output, UNSAFE.getLong(outputBase, matchAddress)); - matchAddress += SIZE_OF_LONG; - output += SIZE_OF_LONG; - } - - while (output < matchOutputLimit) { - UNSAFE.putByte(outputBase, output++, UNSAFE.getByte(outputBase, matchAddress++)); - } - } - else { - do { - UNSAFE.putLong(outputBase, output, UNSAFE.getLong(outputBase, matchAddress)); - matchAddress += SIZE_OF_LONG; - output += SIZE_OF_LONG; - } - while (output < matchOutputLimit); - } - - output = matchOutputLimit; // correction in case we overcopied - } - - return (int) (output - outputAddress); - } -} diff --git a/src/aircompressor/main/java/io/airlift/compress/lz4/UnsafeUtil.java b/src/aircompressor/main/java/io/airlift/compress/lz4/UnsafeUtil.java deleted file mode 100644 index 22cf8bb..0000000 --- a/src/aircompressor/main/java/io/airlift/compress/lz4/UnsafeUtil.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.airlift.compress.lz4; - -import sun.misc.Unsafe; - -import java.lang.reflect.Field; -import java.nio.Buffer; - -final class UnsafeUtil -{ - public static final Unsafe UNSAFE; - private static final Field ADDRESS_ACCESSOR; - - private UnsafeUtil() {} - - static { - try { - Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafe.setAccessible(true); - UNSAFE = (Unsafe) theUnsafe.get(null); - } - catch (Exception e) { - throw new RuntimeException(e); - } - - try { - Field field = Buffer.class.getDeclaredField("address"); - field.setAccessible(true); - ADDRESS_ACCESSOR = field; - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - - public static long getAddress(Buffer buffer) - { - try { - return (long) ADDRESS_ACCESSOR.get(buffer); - } - catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } -} diff --git a/src/ddsutils/compression/ARGBBufferDecompressor.java b/src/ddsutils/compression/ARGBBufferDecompressor.java deleted file mode 100644 index f35ada3..0000000 --- a/src/ddsutils/compression/ARGBBufferDecompressor.java +++ /dev/null @@ -1,53 +0,0 @@ -package compression; - -import java.awt.Dimension; -import java.nio.ByteBuffer; - -public class ARGBBufferDecompressor extends BufferDecompressor{ - - int pixelformat; - - /** - * @param compressedBuffer - * @param width - * @param height - * @param pixelformat - */ - public ARGBBufferDecompressor(final ByteBuffer compressedBuffer, - final int width, final int height, int pixelformat) { - this(compressedBuffer, new Dimension(width, height), pixelformat); - } - - - /** - * @param compressedData - * @param width - * @param height - * @param pixelformat - */ - public ARGBBufferDecompressor(byte[] compressedData, int width, int height, - int pixelformat) { - this(ByteBuffer.wrap(compressedData), new Dimension(width, height), pixelformat); - } - - /** - * @param databuffer - * @param dimension - * @param type - */ - public ARGBBufferDecompressor(final ByteBuffer databuffer, - final Dimension dimension, int pixelformat) { - this.uncompressedBuffer = - decompressBuffer(databuffer, dimension.width, dimension.height, pixelformat); - this.dimension = dimension; - this.pixelformat = pixelformat; - } - - - private ByteBuffer decompressBuffer(ByteBuffer dataBuffer, int width, - int height, Object pix) { - - return dataBuffer; - } - -} diff --git a/src/ddsutils/compression/BufferDecompressor.java b/src/ddsutils/compression/BufferDecompressor.java deleted file mode 100644 index 4f3ecaf..0000000 --- a/src/ddsutils/compression/BufferDecompressor.java +++ /dev/null @@ -1,27 +0,0 @@ -package compression; - -import java.awt.Dimension; -import java.awt.image.BufferedImage; -import java.nio.ByteBuffer; - -import ddsutil.ByteBufferedImage; - -public abstract class BufferDecompressor { - - - protected ByteBuffer uncompressedBuffer; - protected Dimension dimension; - - - /** - * @return - */ - public BufferedImage getImage() { - - BufferedImage image = new ByteBufferedImage( - this.dimension.width, - this.dimension.height, - this.uncompressedBuffer); - return image; - } -} diff --git a/src/ddsutils/compression/DXTBufferCompressor.java b/src/ddsutils/compression/DXTBufferCompressor.java deleted file mode 100644 index 51e5155..0000000 --- a/src/ddsutils/compression/DXTBufferCompressor.java +++ /dev/null @@ -1,278 +0,0 @@ -/** - * - */ -package compression; - -import gr.zdimensions.jsquish.Squish; -import gr.zdimensions.jsquish.Squish.CompressionType; - -import java.awt.Dimension; -import java.awt.image.BufferedImage; -import java.nio.ByteBuffer; -import java.util.zip.DataFormatException; - -import ddsutil.ByteBufferedImage; - - -/** - * Compressor for DXT-Compression - * @author danielsenff - * - */ -public class DXTBufferCompressor { - -// byte[] compressedData; - protected byte[] byteData; - protected int[] intData; - protected Dimension dimension; - protected CompressionType compressionType; - - /** - * @param data Byte-Array should store ARGB - * @param width - * @param height - * @param compressionType - */ - public DXTBufferCompressor(final byte[] data, - final int width, - final int height, - final Squish.CompressionType compressionType) { - this(data, new Dimension(width, height), compressionType); - } - - /** - * @param byteBuffer ByteBuffer should store ARGB - * @param width - * @param height - * @param compressionType - */ - public DXTBufferCompressor(final ByteBuffer byteBuffer, - final int width, - final int height, - final Squish.CompressionType compressionType) { - this(toByteArray(byteBuffer), - new Dimension(width, height), - compressionType); - } - - - /** - * @param image - * @param compressionType - */ - public DXTBufferCompressor(final BufferedImage image, - final Squish.CompressionType compressionType) { - - this(ByteBufferedImage.convertBIintoARGBArray((BufferedImage) image), - new Dimension(image.getWidth(null), image.getHeight(null)), - compressionType ); - } - - /** - * @param data Byte-Array should store ARGB - * @param dimension - * @param compressionType - */ - public DXTBufferCompressor(final byte[] data, - final Dimension dimension, - final Squish.CompressionType compressionType) { - this.byteData = data; - this.dimension = dimension; - this.compressionType = compressionType; - } - - /** - * @param data - * @param dimension - * @param compressionType - */ - public DXTBufferCompressor(final int[] data, - final Dimension dimension, - final Squish.CompressionType compressionType) { - this.intData = data; - this.dimension = dimension; - this.compressionType = compressionType; - } - - - /** - * @return ByteBuffer - */ - public ByteBuffer getByteBuffer() { - byte[] compressedData; - try { - - // the data-Array given to the squishCompressToArray is expected to be - // width * height * 4 -> with RGBA, which means, if we got RGB, we need to add A! - if(byteData.length < dimension.height*dimension.width*4) { - System.out.println("blow up array from RGB to ARGB"); - byteData = convertRGBArraytiRGBAArray(byteData, dimension); - } - - compressedData = squishCompressToArray(byteData, dimension.width, dimension.height, compressionType); - return ByteBuffer.wrap(compressedData); - } catch (DataFormatException e) { - e.printStackTrace(); - } - return null; - - } - - private byte[] convertRGBArraytiRGBAArray(byte[] data, final Dimension dimension) { - - int rgbLength = data.length; - int rgbaLength = dimension.width * dimension.height * 4; - - byte[] rgbaBuffer = new byte[rgbaLength]; - - // populate new array - // we always copy 3 byte chunks, skip one byte, which we set to 255 and take the next 3 byte - int loopN = 0; - for (int i = 0; i < rgbLength; i=i+3) { - - int srcPos = i; - int destPos = i+loopN; - - System.arraycopy(data, srcPos, rgbaBuffer, destPos, 3); - loopN++; - } - - - return rgbaBuffer; - } - - /** - * Get the Byte-array held by this object. - * @return - */ - public byte[] getArray() { - try { - return squishCompressToArray(byteData, dimension.width, dimension.height, compressionType); - } catch (final DataFormatException e) { - e.printStackTrace(); - } - return byteData; - } - - /** - * Compresses the RGBA-byte-array into a DXT-compressed {@link ByteBuffer}. - * @param rgba - * @param height - * @param width - * @param compressionType - * @return - */ -// private static ByteBuffer squishCompress(final byte[] rgba, -// final int width, -// final int height, -// final Squish.CompressionType compressionType) { -// -// -// ByteBuffer buffer = ByteBuffer.wrap(squishCompressToArray(rgba, width, height, compressionType)); -// return buffer; -// } - - - /** - * Compresses the RGBA-byte-array into a DXT-compressed byte-array. - * @param rgba Byte-Array needs to be in RGBA-order - * @param height - * @param width - * @param compressionType - * @return - * @throws DataFormatException - */ - private static byte[] squishCompressToArray(final byte[] rgba, - final int width, - final int height, - final Squish.CompressionType compressionType) throws DataFormatException { - - // expected array length - int length = width * height * 4; - if (rgba.length != length) throw new DataFormatException("unexpected length:" + - rgba.length + " instead of "+ length); - - int storageRequirements = Squish.getStorageRequirements(width, height, compressionType); - - return Squish.compressImage(rgba, - width, - height, - new byte[storageRequirements], - compressionType, - Squish.CompressionMethod.CLUSTER_FIT); - } - - /*private static byte[] squishCompressToArray(final int[] rgba, - final int width, - final int height, - final Squish.CompressionType compressionType) throws DataFormatException { - - // expected array length - int length = width * height; - if (rgba.length != length) throw new DataFormatException("unexpected length:" + - rgba.length + " instead of "+ length); - - int storageRequirements = Squish.getStorageRequirements(width, height, compressionType); - - return Squish.compressImage(rgba, - width, - height, - new byte[storageRequirements], - compressionType, - Squish.CompressionMethod.CLUSTER_FIT); - } */ - - - /** - * Compresses a {@link ByteBuffer} into a DXT-compressed {@link ByteBuffer} - * @param buffer - * @param width - * @param height - * @param compressionType - * @return - */ -// private static ByteBuffer squishCompress(final ByteBuffer bytebuffer, -// final int width ,final int height, final Squish.CompressionType compressionType) { -// -// //byte[] rgba = toByteArray(bytebuffer); -// return squishCompress(byteBuffer, width, height, compressionType); -// } - - private static byte[] toByteArray(final ByteBuffer bytebuffer) { - byte[] rgba = new byte[bytebuffer.capacity()]; - bytebuffer.get(rgba); - return rgba; - } - - /** - * @return - */ - public int getStorageRequirements() { - return getStorageRequirements(dimension, compressionType); - - } - - /** - * Return the length of the required {@link ByteBuffer} for the image - * @param width - * @param height - * @param type - * @return - */ - public static int getStorageRequirements(final int width, final int height, - final Squish.CompressionType type) { - return Squish.getStorageRequirements(width, height, type); - } - - /** - * Return the length of the required {@link ByteBuffer} for the image - * @param imageDimension - * @param type - * @return - */ - public static int getStorageRequirements(final Dimension imageDimension, - final Squish.CompressionType type) { - return Squish.getStorageRequirements((int)imageDimension.getWidth(), (int)imageDimension.getHeight(), type); - } - -} diff --git a/src/ddsutils/compression/DXTBufferDecompressor.java b/src/ddsutils/compression/DXTBufferDecompressor.java deleted file mode 100644 index aa9621b..0000000 --- a/src/ddsutils/compression/DXTBufferDecompressor.java +++ /dev/null @@ -1,126 +0,0 @@ -/** - * - */ -package compression; - -import gr.zdimensions.jsquish.Squish; -import gr.zdimensions.jsquish.Squish.CompressionType; - -import java.awt.Dimension; -import java.nio.ByteBuffer; - - -/** - * Decompressor for DXT-Compression - * @author danielsenff - * - */ -public class DXTBufferDecompressor extends BufferDecompressor{ - - protected CompressionType compressionType; - - - /** - * @param compressedBuffer - * @param width - * @param height - * @param pixelformat - */ - public DXTBufferDecompressor(final ByteBuffer compressedBuffer, - final int width, final int height, CompressionType type) { - this(compressedBuffer, new Dimension(width, height), type); - } - - - /** - * @param compressedData - * @param width - * @param height - * @param compressionType - */ - public DXTBufferDecompressor(byte[] compressedData, int width, int height, - CompressionType compressionType) { - this(ByteBuffer.wrap(compressedData), new Dimension(width, height), compressionType); - } - - /** - * @param compressedBuffer - * @param dimension - * @param type - */ - public DXTBufferDecompressor(final ByteBuffer compressedBuffer, - final Dimension dimension, CompressionType type) { - this.uncompressedBuffer = - squishDecompressBuffer(compressedBuffer, dimension.width, dimension.height, type); - this.dimension = dimension; - this.compressionType = type; - - } - - - /** - * Compresses a Byte-Array into a DXT-compressed {@link ByteBuffer} - * If the type is null, it returns the uncompressed ByteBuffer. - * - * Decompresses a DXT-compressed Byte-Array and returns a byte-Array. - * If the {@link CompressionType} is null, it return the source data. - * @param compressedData - * @param width - * @param height - * @param type - * @return byte[] - * @throws OutOfMemoryError - */ - public static byte[] squishDecompressToArray(final byte[] compressedData, final int width, final int height, - final Squish.CompressionType type) throws OutOfMemoryError { - - //Use JSquish to decompress images. Then bind as normal. - if (type != null) { - byte[] decompressedData = Squish.decompressImage(null, width, height, compressedData, type); - return decompressedData; - } - - return compressedData; - - } - - - - /** - * Decompresses a DXT-compressed Byte-Array and returns a ByteBuffer - * If the {@link CompressionType} is null, it return the source data - * @param compressedData - * @param width - * @param height - * @param type - * @return - * @throws OutOfMemoryError - */ - public static ByteBuffer squishDecompress(final byte[] compressedData, final int width, final int height, - final Squish.CompressionType type) throws OutOfMemoryError { - - return ByteBuffer.wrap(squishDecompressToArray(compressedData, width, height, type)); - } - - - - /** - * Decompresses a DXT-compressed {@link ByteBuffer} - * @param byteBuffer - * @param width - * @param height - * @param type - * @return - * @throws OutOfMemoryError - */ - private static ByteBuffer squishDecompressBuffer(final ByteBuffer byteBuffer, - final int width, final int height, - final Squish.CompressionType type) throws OutOfMemoryError { - - byte[] data = new byte[byteBuffer.capacity()]; - byteBuffer.get(data); - - return squishDecompress(data, width, height, type); - } - -} diff --git a/src/ddsutils/compression/DXTCompression.java b/src/ddsutils/compression/DXTCompression.java deleted file mode 100644 index 7928420..0000000 --- a/src/ddsutils/compression/DXTCompression.java +++ /dev/null @@ -1,109 +0,0 @@ -/** - * - */ -package compression; - -import gr.zdimensions.jsquish.Squish; - -import java.awt.Dimension; -import java.nio.ByteBuffer; - - -/** - * @author danielsenff - * - */ -public class DXTCompression { - - - - - /** - * Return the length of the required {@link ByteBuffer} for the image - * @param width - * @param height - * @param type - * @return - */ - public static int getStorageRequirements(final int width, final int height, final Squish.CompressionType type) { - return Squish.getStorageRequirements(width, height, type); - } - - /** - * Return the length of the required {@link ByteBuffer} for the image - * @param imageDimension - * @param type - * @return - */ - public static int getStorageRequirements(final Dimension imageDimension, final Squish.CompressionType type) { - return Squish.getStorageRequirements((int)imageDimension.getWidth(), (int)imageDimension.getHeight(), type); - } - - - - - - - - - /** - * Compresses the RGBA-byte-array into a DXT-compressed byte-array. - * @param rgba - * @param height - * @param width - * @param compressionType - * @return - */ - public static byte[] squishCompressToArray(final byte[] rgba, - final int width ,final int height, - final Squish.CompressionType compressionType) { - - int storageRequirements = Squish.getStorageRequirements(width, height, compressionType); - - return Squish.compressImage(rgba, - width, - height, - new byte[storageRequirements], - compressionType, - Squish.CompressionMethod.CLUSTER_FIT); - } - - /** - * Compresses the RGBA-byte-array into a DXT-compressed {@link ByteBuffer}. - * @param rgba - * @param height - * @param width - * @param compressionType - * @return - */ - public static ByteBuffer squishCompress(final byte[] rgba, - final int width ,final int height, final Squish.CompressionType compressionType) { - - int storageRequirements = Squish.getStorageRequirements(width, height, compressionType); - ByteBuffer buffer = ByteBuffer.allocateDirect(storageRequirements); - buffer.put(Squish.compressImage(rgba, - width, - height, - new byte[storageRequirements], - compressionType, - Squish.CompressionMethod.CLUSTER_FIT)); - buffer.rewind(); - return buffer; - } - - /** - * @param bytebuffer - * @param width - * @param height - * @param compressionType - * @return - */ - public static ByteBuffer squishCompress(final ByteBuffer bytebuffer, - final int width ,final int height, final Squish.CompressionType compressionType) { - - byte[] rgba = new byte[bytebuffer.capacity()]; - bytebuffer.get(rgba); - return squishCompress(rgba, width, height, compressionType); - } - -} diff --git a/src/ddsutils/ddsutil/BIUtil.java b/src/ddsutils/ddsutil/BIUtil.java deleted file mode 100644 index 444c9f3..0000000 --- a/src/ddsutils/ddsutil/BIUtil.java +++ /dev/null @@ -1,96 +0,0 @@ -/** - * - */ -package ddsutil; - -import java.awt.Graphics; -import java.awt.Image; -import java.awt.image.BufferedImage; - -import ddsutil.ImageOperations.ChannelMode; - - -/** - * @author danielsenff - * - */ -public class BIUtil { - - private BIUtil() {} - - /** - * Extracts the specified {@link ChannelMode} from a {@link BufferedImage} - * and returns it in a new {@link BufferedImage} - * @param sourceBi - * @param channelMode - * @return - */ - public static BufferedImage getChannel(final BufferedImage sourceBi, final ChannelMode channelMode) { - - BufferedImage newBi = new BufferedImage(sourceBi.getWidth(), - sourceBi.getHeight(), BufferedImage.TYPE_3BYTE_BGR); - - /*int pixelcount = sourceBi.getWidth() * sourceBi.getHeight(); - for (int y = 0; y < sourceBi.getHeight(); y++) { - for (int x = 0; x < sourceBi.getWidth(); x++) { - int pixel = sourceBi.getColorModel().getBlue(p); - newBi.setRGB(x, y, ); - } - }*/ - - - for (int y = 0; y < sourceBi.getHeight(); y++) { - for (int x = 0; x < sourceBi.getWidth(); x++) { - -// ColorModel color = sourceBi.getColorModel(); - int[] argb = ImageOperations.readPixelARGB(sourceBi.getRGB(x,y)); - - switch(channelMode){ - case ALPHA: - newBi.setRGB(x, y, ImageOperations.writePixelRGB(argb[0],argb[0],argb[0])); - break; - case RED: - newBi.setRGB(x, y, ImageOperations.writePixelRGB(argb[1],0,0)); - break; - case GREEN: - newBi.setRGB(x, y, ImageOperations.writePixelRGB(0,argb[2],0)); - break; - case BLUE: - newBi.setRGB(x, y, ImageOperations.writePixelRGB(0,0, argb[3])); - break; - case RGB: - newBi.setRGB(x,y, ImageOperations.writePixelARGB(255, argb[1],argb[2],argb[3])); - break; - } - } - } - - return newBi; - } - - /** - * Get an {@link BufferedImage} from an {@link Image}-Object - * @param image - * @param type - * @return - */ - public static BufferedImage convertImageToBufferedImage(final Image image, final int type) { - BufferedImage result = new BufferedImage( - image.getWidth(null), image.getHeight(null), type); - Graphics g = result.createGraphics(); - g.drawImage(image, 0, 0, null); - g.dispose(); - return result; - } - - /** - * Extracts the Alpha channel from a {@link ChannelMode} - * and returns it in a new BufferedImage - * @param sourceBi - * @return - */ - public static Image getAlphaChannel(final BufferedImage sourceBi) { - return getChannel(sourceBi, ChannelMode.ALPHA); - } - -} diff --git a/src/ddsutils/ddsutil/ByteBufferedImage.java b/src/ddsutils/ddsutil/ByteBufferedImage.java deleted file mode 100644 index 106d85c..0000000 --- a/src/ddsutils/ddsutil/ByteBufferedImage.java +++ /dev/null @@ -1,218 +0,0 @@ -/** - * - */ -package ddsutil; - -import java.awt.image.BufferedImage; -import java.awt.image.DataBuffer; -import java.awt.image.DataBufferByte; -import java.awt.image.DataBufferInt; -import java.awt.image.WritableRaster; -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.IntBuffer; - -import javax.activation.UnsupportedDataTypeException; - -import util.ImageUtils; - - - - -/** - * @author danielsenff - * - */ -public class ByteBufferedImage extends BufferedImage { - - /** - * @param width - * @param height - * @param type - */ - public ByteBufferedImage(int width, int height, int type) { - super(width, height, type); - } - - /** - * Creates a BufferedImage with 4byte ARGB. - * @param width - * @param height - * @param buffer - */ - public ByteBufferedImage(final int width, final int height, final Buffer buffer) { - super(width, height, BufferedImage.TYPE_4BYTE_ABGR); - initRaster(width, height, buffer); - } - - /** - * @param width - * @param height - * @param pixels - */ - public ByteBufferedImage(final int width, final int height, final int[] pixels) { - super(width, height, BufferedImage.TYPE_4BYTE_ABGR); - initRaster(width, height, IntBuffer.wrap(pixels)); - } - - /** - * @param width - * @param height - * @param argb - */ - public ByteBufferedImage(final int width, final int height, final byte[] argb) { - super(width, height, BufferedImage.TYPE_4BYTE_ABGR); - initRaster(width, height, ByteBuffer.wrap(argb)); - } - - private void initRaster(int width, int height, Buffer buffer) { - WritableRaster wr = this.getRaster(); - byte[] rgba = new byte[buffer.capacity()]; - ((ByteBuffer)buffer).get(rgba); - wr.setDataElements(0,0, width, height, rgba); - } - - /** - * @return - */ - public byte[] getARGBPixels(){ - return convertBIintoARGBArray(this); - } - - - /** - * @param bi - * @return - */ - public static int[] convertBIintoIntArray(final BufferedImage bi) { - WritableRaster r = bi.getRaster(); - DataBuffer db = r.getDataBuffer(); - if (db instanceof DataBufferInt) { - DataBufferInt dbi = (DataBufferInt) db; - return dbi.getData(); - } - System.err.println("db is of type " + db.getClass()); - return null; - } - - - /** - * Transfers the pixel-Information from a {@link BufferedImage} into a byte-array. - * If the {@link BufferedImage} is of different type, the pixels are reordered and stored in RGBA-order. - * @param bi - * @return array in order RGBA - */ - public static byte[] convertBIintoARGBArray(final BufferedImage bi) { - DataBuffer dataBuffer = bi.getRaster().getDataBuffer(); - - // read channel count - int componentCount = bi.getColorModel().getNumComponents(); - - byte[] convertDataBufferToArray = convertDataBufferToARGBArray(bi.getWidth(), - bi.getHeight(), dataBuffer, componentCount, bi.getType()); - return convertDataBufferToArray; - } - - /** - * I need to manually define the order in my array, because for different - * file formats, this varies and ImageIO doesn't return always the same. - * @param width - * @param height - * @param dataBuffer - * @param componentCount - * @param bufferedImageType - * @return - */ - private static byte[] convertDataBufferToARGBArray(final int width, - final int height, - final DataBuffer dataBuffer, - int componentCount, - final int bufferedImageType) { - int length = height * width * 4; - byte[] argb = new byte[length]; - - int r, g, b, a; - int count = 0; -// if() TODO FIXME, what is the other supported? -// throw new UnsupportedDataTypeException("BufferedImages types TYPE_4BYTE_ABGR supported") - if(length != dataBuffer.getSize()) - throw new IllegalStateException("Databuffer has not the expected length: " + dataBuffer.getSize()+ " instead of " + length); - - for (int i = 0; i < dataBuffer.getSize(); i=i+componentCount) { - // databuffer has unsigned integers, they must be converted to signed byte - // original order from BufferedImage -// - if(componentCount > 3) { - // 32bit image - if (bufferedImageType != BufferedImage.TYPE_4BYTE_ABGR) { - /* working with png+alpha */ - a = (dataBuffer.getElem(i) ); - r = (dataBuffer.getElem(i+1)); - g = (dataBuffer.getElem(i+2)); - b = (dataBuffer.getElem(i+3)); - } else { - /* not working with png+alpha */ - b = (dataBuffer.getElem(i) ); - g = (dataBuffer.getElem(i+1)); - r = (dataBuffer.getElem(i+2)); - a = (dataBuffer.getElem(i+3)); - } - - argb[i] = (byte) (a & 0xFF); - argb[i+1] = (byte) (r & 0xFF); - argb[i+2] = (byte) (g & 0xFF); - argb[i+3] = (byte) (b & 0xFF); - } - else - { //24bit image - - b = (dataBuffer.getElem(count)); - count++; - g = (dataBuffer.getElem(count)); - count++; - r = (dataBuffer.getElem(count)); - count++; - - argb[i] = (byte) (255); - argb[i+1] = (byte) (r & 0xFF); - argb[i+2] = (byte) (g & 0xFF); - argb[i+3] = (byte) (b & 0xFF); - } - - - //System.out.println(argb[i] + " " + argb[i+1] + " " + argb[i+2] + " " + argb[i+3]); - } - // aim should be ARGB order - return argb; - } - - /** - * Compliments by Marvin Fröhlich - * @param srcBI - * @param trgBI - */ - private static void moveARGBtoABGR(final BufferedImage srcBI, final BufferedImage trgBI) { - int[] srcData = ( (DataBufferInt)srcBI.getData().getDataBuffer() ).getData(); - byte[] trgData = ( (DataBufferByte)trgBI.getData().getDataBuffer() ).getData(); - final int size = srcData.length; - for ( int i = 0; i > size;i++ ) { - trgData[i * 4 + 0] = (byte)( ( srcData[i] & 0xFF000000 ) >> 24 ); - trgData[i * 4 + 1] = (byte) ( srcData[i] & 0x000000FF ); - trgData[i * 4 + 2] = (byte)( ( srcData[i] & 0x0000FF00 ) >> 8 ); - trgData[i * 4 + 3] = (byte)( ( srcData[i] & 0x00FF0000 ) >> 16 ); - } - } - - -// public static byte[] intArraytobyteArry(int[] srcArray) { -// byte[] byteArray = new byte[srcArray.length*4]; -// for (int i = 0; i < srcArray.length; i++) { -// trgData[i * 4 + 0] = (byte) ( srcData & 0xFF000000 ) >> 24 ); -// trgData[i * 4 + 1] = (byte) ( srcData & 0x000000FF ); -// trgData[i * 4 + 2] = (byte)( ( srcData & 0x0000FF00 ) >> 8 ); -// trgData[i * 4 + 3] = (byte)( ( srcData & 0x00FF0000 ) >> 16 ); -// } -// } - - -} diff --git a/src/ddsutils/ddsutil/DDSUtil.java b/src/ddsutils/ddsutil/DDSUtil.java deleted file mode 100644 index 7f5c521..0000000 --- a/src/ddsutils/ddsutil/DDSUtil.java +++ /dev/null @@ -1,302 +0,0 @@ -package ddsutil; - - -import gr.zdimensions.jsquish.Squish; - -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.FileLockInterruptionException; - -import javax.activation.UnsupportedDataTypeException; - -import jogl.DDSImage; -import jogl.TEXImage; -import model.TextureMap; -import util.FileUtil; -import util.ImageUtils; - -import compression.DXTBufferCompressor; -import compression.DXTBufferDecompressor; - - - -/** - * Easy loading, saving and manipulation of DDSImages and DXT-Compression. - * - * @author danielsenff - * - */ -public class DDSUtil { - - - /** - * Topmost MipMap Index - */ - public static final int TOP_MOST_MIP_MAP = 0; - - private DDSUtil() { } - - @Deprecated - public static BufferedImage decompressTexture(final File file) throws IOException { - return read(file); - } - - /** - * Create a {@link BufferedImage} from a DXT-compressed - * dds-texture {@link FileLockInterruptionException}. - * @param file - * @return - * @throws IOException - */ - public static BufferedImage read(final File file) throws IOException { - if(file.getName().endsWith(".dds")) - return loadBufferedImage(DDSImage.read(file)); - else - return loadBufferedImage(TEXImage.read(file)); - } - - /** - * Create a {@link BufferedImage} from a DXT-compressed {@link DDSImage} - * @param image - * @return - * @throws UnsupportedDataTypeException - */ - public static BufferedImage loadBufferedImage (final DDSImage image) throws UnsupportedDataTypeException { - if (image.isCompressed()) - return decompressTexture( - image.getMipMap(0).getData(), - image.getWidth(), - image.getHeight(), - findCompressionFormat(image)); - else - return loadBufferedImageFromByteBuffer( - image.getMipMap(0).getData(), - image.getWidth(), - image.getHeight(), - image); - } - - /** - * @param data - * @param width - * @param height - * @param ddsimage - * @return - */ - public static BufferedImage loadBufferedImageFromByteBuffer( - ByteBuffer data, int width, int height, - DDSImage ddsimage) { - - // check pixelformat - if(ddsimage.getPixelFormat() == DDSImage.D3DFMT_A8R8G8B8) { - // data in buffer in 4 byte chunks ordered ARGB - ByteBufferedImage bi = new ByteBufferedImage(width, height, data); - - } else if(ddsimage.getPixelFormat() == DDSImage.D3DFMT_A8R8G8B8) { - - - } - - - // if weird or unknown check RGB-offsets - - - return null; - } - - /** - * Create a {@link BufferedImage} from a DXT-compressed {@link DDSImage} - * @param image - * @return - * @throws UnsupportedDataTypeException - */ - @Deprecated - public static BufferedImage decompressTexture (final DDSImage image) throws UnsupportedDataTypeException { - return loadBufferedImage(image); - } - - /** - * Create a {@link BufferedImage} from a DXT-compressed {@link TEXImage} - * @param image - * @return - * @throws UnsupportedDataTypeException - */ - public static BufferedImage loadBufferedImage(final TEXImage image) throws UnsupportedDataTypeException { - return decompressTexture( - image.getEmbeddedMaps(0).getMipMap(0).getData(), - image.getWidth(), - image.getHeight(), - findCompressionFormat(image.getEmbeddedMaps(0))); - } - - /** - * Create a {@link BufferedImage} from a DXT-compressed {@link TEXImage} - * @param image - * @return - * @throws UnsupportedDataTypeException - */ - @Deprecated - public static BufferedImage decompressTexture (final TEXImage image) throws UnsupportedDataTypeException { - return loadBufferedImage(image); - } - - /** - * Create a {@link BufferedImage} from a DXT-compressed Byte-array. - * @param compressedData - * @param width - * @param height - * @param compressionType - * @return - */ - public static BufferedImage decompressTexture(final byte[] compressedData, - final int width, - final int height, - final Squish.CompressionType compressionType) { - return new DXTBufferDecompressor(compressedData, width, height, compressionType).getImage(); - } - - /** - * Create a {@link BufferedImage} from a DXT-compressed ByteBuffer. - * @param textureBuffer - * @param width - * @param height - * @param compressionType - * @return - */ - public static BufferedImage decompressTexture(final ByteBuffer textureBuffer, - final int width, - final int height, - final Squish.CompressionType compressionType) { - return new DXTBufferDecompressor(textureBuffer, width, height, compressionType).getImage(); - } - - /** - * Create a {@link BufferedImage} from a DXT-compressed ByteBuffer. - * @param textureBuffer - * @param width - * @param height - * @param pixelformat - * @return - * @throws UnsupportedDataTypeException - */ - public static BufferedImage decompressTexture(final ByteBuffer textureBuffer, - final int width, - final int height, - final int pixelformat) throws UnsupportedDataTypeException { - Squish.CompressionType compressionType = PixelFormats.getSquishCompressionFormat(pixelformat); - return new DXTBufferDecompressor(textureBuffer, width, height, compressionType).getImage(); - } - - /** - * Compresses a {@link BufferedImage} into a {@link ByteBuffer} - * @param image - * @param compressionType - * @return - */ - public static ByteBuffer compressTexture(final BufferedImage image, - final Squish.CompressionType compressionType) { - return new DXTBufferCompressor(image, compressionType).getByteBuffer(); - } - - /** - * @param image - * @param compressionType - * @return - */ - public static byte[] compressTextureToArray(final BufferedImage image, - final Squish.CompressionType compressionType) { - return new DXTBufferCompressor(image, compressionType).getArray(); - } - - /** - * Writes a DDS-Image file to disk. - * @param destinationfile - * @param sourceImage - * @param pixelformat - * @param generateMipMaps - * @throws IOException - * - */ - public static void write(final File destinationfile, - BufferedImage sourceImage, - final int pixelformat, - boolean generateMipMaps) throws IOException { - - int width = sourceImage.getWidth(); - int height = sourceImage.getHeight(); - - //convert RGB to RGBA image - if(!sourceImage.getColorModel().hasAlpha()) - sourceImage = ImageUtils.convert(sourceImage, BufferedImage.TYPE_4BYTE_ABGR); - - TextureMap maps = TextureFactory.createTextureMap(generateMipMaps, sourceImage); - - ByteBuffer[] mipmapBuffer = null; - - if (PixelFormats.isDXTCompressed(pixelformat)) { - mipmapBuffer = maps.getDXTCompressedBuffer(pixelformat); - } else - mipmapBuffer = maps.getUncompressedBuffer(); - - writeDDSImage(destinationfile, mipmapBuffer, width, height, pixelformat); - } - - /** - * TODO: what about DXT1? - * @param file - * @param map - * @param pixelformat - * @throws IOException - * - */ - public void write(final File file, - final TextureMap map, - final int pixelformat) throws IOException { - writeDDSImage(file, map.getDXTCompressedBuffer(pixelformat), - map.getWidth(), - map.getHeight(), - pixelformat); - } - - private static DDSImage writeDDSImage(final File file, - final ByteBuffer[] mipmapBuffer, - final int width, - final int height, - final int pixelformat) throws IllegalArgumentException, IOException { - - DDSImage writedds = DDSImage.createFromData(pixelformat, width, height, mipmapBuffer); -// writedds.debugPrint(); - writedds.write(file); - return writedds; - } - - /** - * Returns the PixelFormat of a {@link File} - * This makes file-IO, therefor handle with caution! - * @param file - * @return - * @throws IOException - */ - public static int getCompressionType(final File file) throws IOException { - return DDSImage.read(file).getPixelFormat(); - } - - private static Squish.CompressionType findCompressionFormat(DDSImage ddsimage) throws UnsupportedDataTypeException { - int pixelFormat = ddsimage.getPixelFormat(); - return PixelFormats.getSquishCompressionFormat(pixelFormat); - } - - /** - * Returns true for file formats supported by this library. - * Currently TEX and DDS reading is supported. - * @param file - * @return - */ - public static boolean isReadSupported(final File file) { - String fileSuffix = FileUtil.getFileSuffix(file); - return fileSuffix.endsWith("dds") - || fileSuffix.endsWith("tex"); - } -} diff --git a/src/ddsutils/ddsutil/ImageOperations.java b/src/ddsutils/ddsutil/ImageOperations.java deleted file mode 100644 index 6d4246a..0000000 --- a/src/ddsutils/ddsutil/ImageOperations.java +++ /dev/null @@ -1,242 +0,0 @@ -package ddsutil; - -import java.awt.image.BufferedImage; -import java.awt.image.Raster; -import java.awt.image.WritableRaster; -import java.nio.Buffer; -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.nio.IntBuffer; - - -/** - * General image functions for handling Images, BufferedImages and Arrays - * @author danielsenff - * - */ -public class ImageOperations { - - /** - * Possible Display modes to select a specific channel. - * So far only RGB is supported. - */ - public enum ChannelMode { - /** - * Display RGB with screen-overlayed Alpha-Channel. - */ RGBA, - /** - * RGB without Alpha - */ RGB, - /** - * Red color channel - */ RED, - /** - * Green color channel - */ GREEN, - /** - * Blue color channel - */ BLUE, - /** - * Alpha color channel - */ ALPHA } - - - private ImageOperations() {} - - /** - * Reads RGB colors from a single int color value. - * @param c int with 3 colors included - * @return int-array with RGB-values - */ - public static int[] readPixelRGB(final int c) { - int[] color = { - 255, - (c & 0x00ff0000) >> 16, - (c & 0x0000ff00) >> 8, - (c & 0x000000ff)}; - return color; - } - - - /** - * Reads ARGB colors from a single int color value. - * @param c int with 4 colors included - * @return int-array with ARGB-values - */ - public static int[] readPixelARGB(final int c) { - // this is dirty I think, I got the problem, that the a- value gets -1, instead of 255 - // so I use this method to get a positive value instead ... - int a = unsignedByteToInt((byte) ((c & 0xff000000) >> 24)); -// System.out.println(a); -// System.out.println((c & 0xff000000) >> 24); - int[] color = { - a, //a - (c & 0x00ff0000) >> 16, //r - (c & 0x0000ff00) >> 8, //g - (c & 0x000000ff)}; //b - return color; - } - - - /** - * Write single color values into one int by byte-shifting. For RGB-pixelformat. - * Returns an 32byte integer with 255 alpha - * @param r - * @param b - * @param g - * @return - */ - public static int writePixelRGB(final int r, final int g, final int b) { - return 0xFF000000 + ((r & 0xff) << 16) + ((g & 0xff) << 8) + ( b & 0xff); - } - - public static int writePixelRGB(final int[] color) { - return writePixelRGB(color[0], color[1], color[2]); - } - - /** - * Write single color values into one int by byte-shifting. For ARGB-pixelsformat - * @param a - * @param r - * @param b - * @param g - * @return - */ - public static int writePixelARGB(final int a, final int r, final int g, final int b) { - return 0x00000000 + ((a & 0xff) << 24) + ((r & 0xff) << 16) + ((g & 0xff) << 8) + ( b & 0xff); - } - - /** - * @param color - * @return - */ - public static int writePixelARGB(final int[] color) { - return writePixelARGB(color[0], color[1], color[2], color[3]); - } - - /** - * Cast a Byte into an unsigned Integer - * @param b - * @return unsigned Integer - */ - public static int unsignedByteToInt(final byte b) { - return (int) b & 0xFF; - } - - - - - /** - * Paint Channel in a specified value (0-255) - * Channel RGBA (0,1,2,3) - * This is for {@link BufferedImage} - * @param bi - * @param channel - * @param color - * @return - */ - public static byte[] paintValueInChannel(final BufferedImage bi, final int channel, final float color) { - return paintChannelInValue(ByteBufferedImage.convertBIintoARGBArray(bi), - bi.getWidth(), - bi.getHeight(), - channel, color); - } - - /** - * Paint a specific RGB-color channel in a color - * Channel RGBA (0,1,2,3) - * This is for {@link ByteBuffer} - * @param bytebuffer - * @param width - * @param height - * @param channel - * @param greyValue - * @return - */ - public static byte[] paintValueInChannel(final ByteBuffer bytebuffer, - final int width, final int height, final int channel, final float greyValue) { - byte[] data = new byte[bytebuffer.capacity()]; - bytebuffer.get(data); - - return paintChannelInValue(data, - width, - height, - channel, greyValue); - } - - - /** - * Paint Channel in a specified value (0-255) - * Channel RGBA (0,1,2,3) - * @param rgba - * @param width - * @param height - * @param channel to paint in - * @param color - * @return - */ - public static byte[] paintChannelInValue(final byte[] rgba, - final int width, final int height, final int channel, final float color) { - - int limit = width * height * 4; - - for (int pos = 0; pos < (limit)-3; pos = pos + 4) { - rgba[pos + channel] = (byte) convertColor(color); - } - - return rgba; - } - - - - /** - * Conversion between float color value to an integer value - * @param value color - * @return - */ - public static int convertColor(final float value) { - return (int) (255*value); - } - - /** - * Conversion between double color value to a integer value - * @param value color - * @return - */ - public static int convertColor(final double value) { - return (int) (255*value); - } - - /** - * Checks a color array if all values are within the possible range of values. - * If the limits are exceeded, the value is set to equal the limit. - * @param color - * @param lowerLimit Upper limit of values - * @param upperLimit Lower limit of values - * @return - */ - public static int [] limitColorBoundaries(int[] color, final int lowerLimit, final int upperLimit) { - for (int i = 0; i < color.length; i++) { - color[i] = checkValueLimits(color[i], lowerLimit, upperLimit); - } - return color; - } - - /** - * Checks if the value is within defined limits. - * @param value - * @param lowerLimit - * @param upperLimit - * @return - */ - public static int checkValueLimits(int value, final int lowerLimit, final int upperLimit) { - if (value < lowerLimit) { - return lowerLimit; - } else if (value > upperLimit) { - return upperLimit; - } else { - return value; - } - } - -} diff --git a/src/ddsutils/ddsutil/ImageRescaler.java b/src/ddsutils/ddsutil/ImageRescaler.java deleted file mode 100644 index 559e450..0000000 --- a/src/ddsutils/ddsutil/ImageRescaler.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * - */ -package ddsutil; - -import java.awt.Image; -import java.awt.image.BufferedImage; - -/** - * Java Graphics2D Rescaler - * @author danielsenff - * - */ -public class ImageRescaler extends Rescaler { - - /** - * Graphics2D Scale algorithm - */ - private int scaleAlgorithm; - - /** - * - */ - public ImageRescaler() { - scaleAlgorithm = Image.SCALE_SMOOTH; - } - - /** - * @param scaleMethod - */ - public ImageRescaler(final int scaleMethod) { - scaleAlgorithm = scaleMethod; - } - - /** - * @param image - * @param width - * @param height - * @return - */ - @Override - public BufferedImage rescaleBI(final BufferedImage originalImage, - final int newWidth, final int newHeight) { - - Image rescaledImage = originalImage.getScaledInstance(newWidth, newHeight, scaleAlgorithm); - BufferedImage bi; - if(rescaledImage instanceof BufferedImage) - bi = (BufferedImage)rescaledImage; - else - bi = BIUtil.convertImageToBufferedImage(rescaledImage, BufferedImage.TYPE_4BYTE_ABGR); - - return bi; - } - - /** - * @return - */ - public int getScaleAlgorithm() { - return this.scaleAlgorithm; - } - - /** - * @param scaleAlgorithm - */ - public void setScaleAlgorithm(final int scaleAlgorithm) { - this.scaleAlgorithm = scaleAlgorithm; - } -} diff --git a/src/ddsutils/ddsutil/MipMapsUtil.java b/src/ddsutils/ddsutil/MipMapsUtil.java deleted file mode 100644 index 84e1a9e..0000000 --- a/src/ddsutils/ddsutil/MipMapsUtil.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * - */ -package ddsutil; - -import java.awt.Dimension; - - -/** - * Some helper methods for MipMap generation - * @author danielsenff - * - */ -public class MipMapsUtil { - - /** - * Topmost MipMap Index - */ - public static final int TOP_MOST_MIP_MAP = 0; - - /** - * Number of MipMaps that will be generated from this image sizes. - * @param width - * @param height - * @return - */ - public static int calculateMaxNumberOfMipMaps(final int width, final int height) { - return ((int) Math.floor(Math.log(Math.max(width, height)) / Math.log(2.0)))+1; // plus original - } - - /** - * Number of MipMaps that will be generated from this image dimension. - * @param dimension - * @return - */ - public static int calculateMaxNumberOfMipMaps(final Dimension dimension) { - return calculateMaxNumberOfMipMaps(dimension.width, dimension.height); - } - - /** - * Checks if a value is a power of two - * @param value - * @return - */ - public static boolean isPowerOfTwo(final int value) { - double p = Math.floor(Math.log(value) / Math.log(2.0)); - double n = Math.pow(2.0, p); - return (n==value); - } - -} diff --git a/src/ddsutils/ddsutil/NonCubicDimensionException.java b/src/ddsutils/ddsutil/NonCubicDimensionException.java deleted file mode 100644 index 1bc6fe8..0000000 --- a/src/ddsutils/ddsutil/NonCubicDimensionException.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * - */ -package ddsutil; - -/** - * @author danielsenff - * - */ -public class NonCubicDimensionException extends IllegalArgumentException { - - /** - * - */ - private static final long serialVersionUID = 1L; - - /** - * - */ - public NonCubicDimensionException() { - super("MipMaps can not be generated, The image dimensions must be a power of 2"); - } - -} diff --git a/src/ddsutils/ddsutil/PixelFormats.java b/src/ddsutils/ddsutil/PixelFormats.java deleted file mode 100644 index e5feaf8..0000000 --- a/src/ddsutils/ddsutil/PixelFormats.java +++ /dev/null @@ -1,187 +0,0 @@ -package ddsutil; - -import gr.zdimensions.jsquish.Squish; - -import javax.activation.UnsupportedDataTypeException; - -import model.TextureImage.PixelFormat; - -import jogl.DDSImage; - -/** - * Collects pixelformat conversions and interfaces. - * This is WIP and not used yet, as it is the prelude to refactoring. - * @author danielsenff - * - */ -public class PixelFormats { - - /** - * - * @param pixelFormat DDSImage pixelformat - * @return - * @throws UnsupportedDataTypeException - */ - public static Squish.CompressionType getSquishCompressionFormat(final int pixelFormat) - throws UnsupportedDataTypeException { - switch(pixelFormat) { - case DDSImage.D3DFMT_DXT1: - return Squish.CompressionType.DXT1; - case DDSImage.D3DFMT_DXT3: - return Squish.CompressionType.DXT3; - case DDSImage.D3DFMT_DXT5: - return Squish.CompressionType.DXT5; - default: - throw new UnsupportedDataTypeException("given pixel format not supported compression format"); - } - } - - /** - * Convert Integer-CompressionType of {@link DDSImage} to {@link Squish}-Enum - * @param compressionType D3DFMT - * @return Squish.CompressionType - */ - public static Squish.CompressionType selectedCompression(final int compressionType) { - - // TODO maybe do as hasmap? -// Hashtable numbers = new Hashtable(); -// numbers.put("one", new Integer(1)); -// numbers.put("two", new Integer(2)); -// numbers.put("three", new Integer(3)); - - - switch(compressionType) { - default: - case DDSImage.D3DFMT_A8R8G8B8: - return null; - case DDSImage.D3DFMT_DXT1: - return Squish.CompressionType.DXT1; - case DDSImage.D3DFMT_DXT3: - return Squish.CompressionType.DXT3; - case DDSImage.D3DFMT_DXT5: - return Squish.CompressionType.DXT5; - case DDSImage.D3DFMT_R8G8B8: - return null; - } - } - - /** - * Returns the verbose Pixelformat this DDSFile for the pixelformat-code - * @param pixelformat - * @return String - */ - public static String verbosePixelformat(final int pixelformat) { - // TODO get rid of such constructs - switch(pixelformat) { - default: - return PixelFormat.Unknown.toString(); - case DDSImage.D3DFMT_A8R8G8B8: - return PixelFormat.Unknown.toString(); - case DDSImage.D3DFMT_DXT1: - return PixelFormat.DXT1.toString(); - case DDSImage.D3DFMT_DXT2: - return PixelFormat.DXT2.toString(); - case DDSImage.D3DFMT_DXT3: - return PixelFormat.DXT3.toString(); - case DDSImage.D3DFMT_DXT4: - return PixelFormat.DXT4.toString(); - case DDSImage.D3DFMT_DXT5: - return PixelFormat.DXT5.toString(); - case DDSImage.D3DFMT_R8G8B8: - return PixelFormat.R8G8B8.toString(); - case DDSImage.D3DFMT_X8R8G8B8: - return PixelFormat.X8R8G8B8.toString(); - } - } - - /** - * Returns the internal Integer-value for the input pixelformat-Name - * @param pixelformatVerbose - * @return - */ - public static int verbosePixelformat(final String pixelformatVerbose) { - // TODO get rid of such constructs - if (pixelformatVerbose.equals(PixelFormat.DXT1.toString())) { - return DDSImage.D3DFMT_DXT1; - } else if (pixelformatVerbose.equals(PixelFormat.DXT2.toString())) { - return DDSImage.D3DFMT_DXT2; - } else if (pixelformatVerbose.equals(PixelFormat.DXT3.toString())) { - return DDSImage.D3DFMT_DXT3; - } else if (pixelformatVerbose.equals(PixelFormat.DXT4.toString())) { - return DDSImage.D3DFMT_DXT4; - } else if (pixelformatVerbose.equals(PixelFormat.DXT5.toString())) { - return DDSImage.D3DFMT_DXT5; - } else if (pixelformatVerbose == PixelFormat.R8G8B8.toString()) { - return DDSImage.D3DFMT_R8G8B8; - } else if (pixelformatVerbose.equals(PixelFormat.X8R8G8B8.toString())) { - return DDSImage.D3DFMT_X8R8G8B8; - } else if (pixelformatVerbose.equals(PixelFormat.A8R8G8B8.toString())) { - return DDSImage.D3DFMT_A8R8G8B8; - } else { - return DDSImage.D3DFMT_UNKNOWN; - } - } - - /** - * TODO get rid of such constructs - * @param pixelformat - * @return - */ - public static int convertPixelformat(final PixelFormat pixelformat) { - int format; - switch(pixelformat) { - default: - case Unknown: - format = DDSImage.D3DFMT_UNKNOWN; - break; - case DXT5: - format = DDSImage.D3DFMT_DXT5; - break; - case DXT4: - format = DDSImage.D3DFMT_DXT4; - break; - case DXT3: - format = DDSImage.D3DFMT_DXT3; - break; - case DXT2: - format = DDSImage.D3DFMT_DXT2; - break; - case DXT1: - format = DDSImage.D3DFMT_DXT1; - break; - case A8R8G8B8: - format = DDSImage.D3DFMT_A8R8G8B8; - break; - case X8R8G8B8: - format = DDSImage.D3DFMT_X8R8G8B8; - break; - case R8G8B8: - format = DDSImage.D3DFMT_R8G8B8; - break; - } - return format; - } - - /** - * Returns true if the pixelformat is compressed a kind of DXTn-Compression - * TODO The {@link DDSImage} specifies isCompressed even on D3DFMT_A8R8G8B8, D3DFMT_R8G8B8 and D3DFMT_X8R8G8B8 - * this doesn't - * @param pixelformat DDSImage pixelformat - * @return boolean is compressed - */ - public static boolean isDXTCompressed(final int pixelformat) { - switch(pixelformat) { - default: - case DDSImage.D3DFMT_A8R8G8B8: - case DDSImage.D3DFMT_R8G8B8: - case DDSImage.D3DFMT_X8R8G8B8: - return false; - case DDSImage.D3DFMT_DXT1: - case DDSImage.D3DFMT_DXT2: - case DDSImage.D3DFMT_DXT3: - case DDSImage.D3DFMT_DXT4: - case DDSImage.D3DFMT_DXT5: - return true; - } - } -} diff --git a/src/ddsutils/ddsutil/Rescaler.java b/src/ddsutils/ddsutil/Rescaler.java deleted file mode 100644 index 444df8a..0000000 --- a/src/ddsutils/ddsutil/Rescaler.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * - */ -package ddsutil; - -import java.awt.image.BufferedImage; - -/** - * Interface for defining and encapsulating image scaling algorithms. - * @author danielsenff - * - */ -public abstract class Rescaler { - - /** - * @param image - * @param width - * @param height - * @return - */ - public abstract BufferedImage rescaleBI(BufferedImage image, int width, int height); - - -} diff --git a/src/ddsutils/ddsutil/TextureFactory.java b/src/ddsutils/ddsutil/TextureFactory.java deleted file mode 100644 index 253447f..0000000 --- a/src/ddsutils/ddsutil/TextureFactory.java +++ /dev/null @@ -1,30 +0,0 @@ -package ddsutil; - -import java.awt.image.BufferedImage; - -import model.MipMaps; -import model.SingleTextureMap; -import model.TextureMap; - -/** - * @author danielsenff - * - */ -public class TextureFactory { - - /** - * @param generateMipMaps - * @param sourceImage - * @return - */ - public static TextureMap createTextureMap(final boolean generateMipMaps, final BufferedImage sourceImage) { - TextureMap maps; - if (generateMipMaps) { - maps = new MipMaps(); - ((MipMaps)maps).generateMipMaps(sourceImage); - } else - maps = new SingleTextureMap(sourceImage); - - return maps; - } -} diff --git a/src/ddsutils/jogl/DDSImage.java b/src/ddsutils/jogl/DDSImage.java deleted file mode 100644 index e26c85a..0000000 --- a/src/ddsutils/jogl/DDSImage.java +++ /dev/null @@ -1,1029 +0,0 @@ -/* - * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. - */ - -package jogl; - -import java.io.*; -import java.nio.*; -import java.nio.channels.*; - - - - - - -/** A reader and writer for DirectDraw Surface (.dds) files, which are - used to describe textures. These files can contain multiple mipmap - levels in one file. This class is currently minimal and does not - support all of the possible file formats. */ - -public class DDSImage { - - /** Simple class describing images and data; does not encapsulate - image format information. User is responsible for transmitting - that information in another way. */ - - public static class ImageInfo { - private ByteBuffer data; - private int width; - private int height; - private boolean isCompressed; - private int compressionFormat; - - public ImageInfo(ByteBuffer data, int width, int height, boolean compressed, int compressionFormat) { - this.data = data; this.width = width; this.height = height; - this.isCompressed = compressed; this.compressionFormat = compressionFormat; - } - public int getWidth() { return width; } - public int getHeight() { return height; } - public ByteBuffer getData() { return data; } - public boolean isCompressed() { return isCompressed; } - public int getCompressionFormat() { - if (!isCompressed()) - throw new RuntimeException("Should not call unless compressed"); - return compressionFormat; - } - } - /** - * http://msdn.microsoft.com/en-us/library/bb943984(v=vs.85).aspx - * @author danielsenff - * - */ - public static class PixelFormat { - public int dwSize; - public int dwFlags; - public int dwFourCC; - public int dwRGBBitCount; - public int dwRBitMask; - public int dwGBitMask; - public int dwBBitMask; - public int dwABitMask; - } - - private FileInputStream fis; - private FileChannel chan; - private ByteBuffer buf; - private Header header; - - // - // Selected bits in header flags - // - - public static final int DDSD_CAPS = 0x00000001; // Capacities are valid - public static final int DDSD_HEIGHT = 0x00000002; // Height is valid - public static final int DDSD_WIDTH = 0x00000004; // Width is valid - public static final int DDSD_PITCH = 0x00000008; // Pitch is valid - public static final int DDSD_BACKBUFFERCOUNT = 0x00000020; // Back buffer count is valid - public static final int DDSD_ZBUFFERBITDEPTH = 0x00000040; // Z-buffer bit depth is valid (shouldn't be used in DDSURFACEDESC2) - public static final int DDSD_ALPHABITDEPTH = 0x00000080; // Alpha bit depth is valid - public static final int DDSD_LPSURFACE = 0x00000800; // lpSurface is valid - public static final int DDSD_PIXELFORMAT = 0x00001000; // ddpfPixelFormat is valid - public static final int DDSD_MIPMAPCOUNT = 0x00020000; // Mip map count is valid - public static final int DDSD_LINEARSIZE = 0x00080000; // dwLinearSize is valid - public static final int DDSD_DEPTH = 0x00800000; // dwDepth is valid - - public static final int DDPF_ALPHAPIXELS = 0x00000001; // Alpha channel is present - public static final int DDPF_ALPHA = 0x00000002; // Only contains alpha information - public static final int DDPF_FOURCC = 0x00000004; // FourCC code is valid - public static final int DDPF_PALETTEINDEXED4 = 0x00000008; // Surface is 4-bit color indexed - public static final int DDPF_PALETTEINDEXEDTO8 = 0x00000010; // Surface is indexed into a palette which stores indices - // into the destination surface's 8-bit palette - public static final int DDPF_PALETTEINDEXED8 = 0x00000020; // Surface is 8-bit color indexed - public static final int DDPF_RGB = 0x00000040; // RGB data is present - public static final int DDPF_COMPRESSED = 0x00000080; // Surface will accept pixel data in the format specified - // and compress it during the write - public static final int DDPF_RGBTOYUV = 0x00000100; // Surface will accept RGB data and translate it during - // the write to YUV data. The format of the data to be written - // will be contained in the pixel format structure. The DDPF_RGB - // flag will be set. - public static final int DDPF_YUV = 0x00000200; // Pixel format is YUV - YUV data in pixel format struct is valid - public static final int DDPF_ZBUFFER = 0x00000400; // Pixel format is a z buffer only surface - public static final int DDPF_PALETTEINDEXED1 = 0x00000800; // Surface is 1-bit color indexed - public static final int DDPF_PALETTEINDEXED2 = 0x00001000; // Surface is 2-bit color indexed - public static final int DDPF_ZPIXELS = 0x00002000; // Surface contains Z information in the pixels - - // Selected bits in DDS capabilities flags - public static final int DDSCAPS_TEXTURE = 0x00001000; // Can be used as a texture - public static final int DDSCAPS_MIPMAP = 0x00400000; // Is one level of a mip-map - public static final int DDSCAPS_COMPLEX = 0x00000008; // Complex surface structure, such as a cube map - - // Selected bits in DDS extended capabilities flags - public static final int DDSCAPS2_CUBEMAP = 0x00000200; - public static final int DDSCAPS2_CUBEMAP_POSITIVEX = 0x00000400; - public static final int DDSCAPS2_CUBEMAP_NEGATIVEX = 0x00000800; - public static final int DDSCAPS2_CUBEMAP_POSITIVEY = 0x00001000; - public static final int DDSCAPS2_CUBEMAP_NEGATIVEY = 0x00002000; - public static final int DDSCAPS2_CUBEMAP_POSITIVEZ = 0x00004000; - public static final int DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x00008000; - public static final int DDSCAPS2_VOLUME = 0x00200000; - - // Known pixel formats - public static final int D3DFMT_UNKNOWN = 0; - public static final int D3DFMT_R8G8B8 = 20; - public static final int D3DFMT_A8R8G8B8 = 21; - public static final int D3DFMT_X8R8G8B8 = 22; - public static final int D3DFMT_R5G6B5 = 23; - public static final int D3DFMT_X1R5G5B5 = 24; - public static final int D3DFMT_A1R5G5B5 = 25; - public static final int D3DFMT_A4R4G4B4 = 26; - public static final int D3DFMT_R3G3B2 = 27; - public static final int D3DFMT_A8 = 28; - public static final int D3DFMT_A8R3G3B2 = 29; - public static final int D3DFMT_X4R4G4B4 = 30; - public static final int D3DFMT_A2B10G10R10 = 31; - public static final int D3DFMT_A8B8G8R8 = 32; - public static final int D3DFMT_X8B8G8R8 = 33; - public static final int D3DFMT_G16R16 = 34; - public static final int D3DFMT_A2R10G10B10 = 35; - public static final int D3DFMT_A16B16G16R16 = 36; - - - //DXGI_FORMAT_R8G8_B8G8_UNORM - public static final int D3DFMT_R8G8_B8G8 = 0x47424752; - - //DXGI_FORMAT_G8R8_G8B8_UNORM - public static final int D3DFMT_G8R8_G8B8 = 0x42475247; - - //DXGI_FORMAT_R16G16B16A16_SNORM - public static final int D3DFMT_Q16W16V16U16 = 110; - - //DXGI_FORMAT_R16_FLOAT - public static final int D3DFMT_R16F = 111; - - //DXGI_FORMAT_R16G16_FLOAT - public static final int D3DFMT_G16R16F = 112; - - //DXGI_FORMAT_R16G16B16A16_FLOAT - public static final int D3DFMT_A16B16G16R16F = 113; - - //DXGI_FORMAT_R32_FLOAT - public static final int D3DFMT_R32F = 114; - - //DXGI_FORMAT_R32G32_FLOAT - public static final int D3DFMT_G32R32F = 115; - - //DXGI_FORMAT_R32G32B32A32_FLOAT - public static final int D3DFMT_A32B32G32R32F = 116; - - public static final int D3DFMT_UYVY = 0x59565955; - public static final int D3DFMT_YUY2 = 0x32595559; - public static final int D3DFMT_CxV8U8 = 117; - - // This is set only by the nvidia exporter, it is not set by the dx texture tool. - // It is ignored by the dx texture tool but it returns the ability to be opened in photoshop so I decided to keep it. - public static final int D3DFMT_Q8W8V8U8 = 63; - - // The following are also valid FourCC codes - public static final int D3DFMT_DXT1 = 0x31545844; - public static final int D3DFMT_DXT2 = 0x32545844; - public static final int D3DFMT_DXT3 = 0x33545844; - public static final int D3DFMT_DXT4 = 0x34545844; - public static final int D3DFMT_DXT5 = 0x35545844; - - // DX10 feature - - public static final int D3D10_RESOURCE_DIMENSION_TEXTURE1D = 2; - public static final int D3D10_RESOURCE_DIMENSION_TEXTURE2D = 3; - public static final int D3D10_RESOURCE_DIMENSION_TEXTURE3D = 4; - - // DX10 pixelformat - public static final int DX10 = 0x30315844; - public static final int DXGI_FORMAT_BC4_UNORM = 0x55344342; - public static final int DXGI_FORMAT_BC4_SNORM = 0x53344342; - public static final int DXGI_FORMAT_BC5_UNORM = 0x32495441; - public static final int DXGI_FORMAT_BC5_SNORM = 0x53354342; - - private enum LoadSurfaceFormat - { - Unknown, - Dxt1, - Dxt3, - Dxt5, - R8G8B8, - B8G8R8, - Bgra5551, - Bgra4444, - Bgr565, - Alpha8, - X8R8G8B8, - A8R8G8B8, - A8B8G8R8, - X8B8G8R8, - RGB555, - R32F, - R16F, - A32B32G32R32F, - A16B16G16R16F, - Q8W8V8U8, - CxV8U8, - G16R16F, - G32R32F, - G16R16, - A2B10G10R10, - A16B16G16R16 - } - - /** Reads a DirectDraw surface from the specified file name, - returning the resulting DDSImage. - - @param filename File name - @return DDS image object - @throws java.io.IOException if an I/O exception occurred - */ - public static DDSImage read(String filename) throws IOException { - return read(new File(filename)); - } - - /** Reads a DirectDraw surface from the specified file, returning - the resulting DDSImage. - - @param file File object - @return DDS image object - @throws java.io.IOException if an I/O exception occurred - */ - public static DDSImage read(File file) throws IOException { - DDSImage image = new DDSImage(); - image.readFromFile(file); - return image; - } - - /** Reads a DirectDraw surface from the specified ByteBuffer, returning - the resulting DDSImage. - - @param buf Input data - @return DDS image object - @throws java.io.IOException if an I/O exception occurred - */ - public static DDSImage read(ByteBuffer buf) throws IOException { - DDSImage image = new DDSImage(); - image.readFromBuffer(buf); - return image; - } - - /** Closes open files and resources associated with the open - DDSImage. No other methods may be called on this object once - this is called. */ - public void close() { - try { - if (chan != null) { - chan.close(); - chan = null; - } - if (fis != null) { - fis.close(); - fis = null; - } - buf = null; - } catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * Creates a new DDSImage from data supplied by the user. The - * resulting DDSImage can be written to disk using the write() - * method. - * - * @param d3dFormat the D3DFMT_ constant describing the data; it is - * assumed that it is packed tightly - * @param width the width in pixels of the topmost mipmap image - * @param height the height in pixels of the topmost mipmap image - * @param mipmapData the data for each mipmap level of the resulting - * DDSImage; either only one mipmap level should - * be specified, or they all must be - * @throws IllegalArgumentException if the data does not match the - * specified arguments - * @return DDS image object - */ - public static DDSImage createFromData(int d3dFormat, - int width, - int height, - ByteBuffer[] mipmapData) throws IllegalArgumentException { - DDSImage image = new DDSImage(); - image.initFromData(d3dFormat, width, height, mipmapData); - return image; - } - - /** Determines from the magic number whether the given InputStream - points to a DDS image. The given InputStream must return true - from markSupported() and support a minimum of four bytes of - read-ahead. - - @param in Stream to check - @return true if input stream is DDS image or false otherwise - @throws java.io.IOException if an I/O exception occurred - */ - public static boolean isDDSImage(InputStream in) throws IOException { - if (!(in instanceof BufferedInputStream)) { - in = new BufferedInputStream(in); - } - if (!in.markSupported()) { - throw new IOException("Can not test non-destructively whether given InputStream is a DDS image"); - } - in.mark(4); - int magic = 0; - for (int i = 0; i < 4; i++) { - int tmp = in.read(); - if (tmp < 0) { - in.reset(); - return false; - } - magic = ((magic >>> 8) | (tmp << 24)); - } - in.reset(); - return (magic == MAGIC); - } - - /** - * Writes this DDSImage to the specified file name. - * @param filename File name to write to - * @throws java.io.IOException if an I/O exception occurred - */ - public void write(String filename) throws IOException { - write(new File(filename)); - } - - public void write(FileOutputStream fos) throws IOException { - FileChannel chan = fos.getChannel(); - // Create ByteBuffer for header in case the start of our - // ByteBuffer isn't actually memory-mapped - ByteBuffer hdr = ByteBuffer.allocate(Header.writtenSize()); - hdr.order(ByteOrder.LITTLE_ENDIAN); - header.write(hdr); - hdr.rewind(); - chan.write(hdr); - buf.position(Header.writtenSize()); - chan.write(buf); - chan.force(true); - chan.close(); - } - - /** - * Writes this DDSImage to the specified file name. - * @param file File object to write to - * @throws java.io.IOException if an I/O exception occurred - */ - public void write(File file) throws IOException { - FileOutputStream stream = new FileOutputStream(file); - write(stream); - stream.close(); - } - - /** Test for presence/absence of surface description flags (DDSD_*) - * @param flag DDSD_* flags set to test - * @return true if flag present or false otherwise - */ - public boolean isSurfaceDescFlagSet(int flag) { - return ((header.flags & flag) != 0); - } - - /** Test for presence/absence of pixel format flags (DDPF_*) */ - public boolean isPixelFormatFlagSet(int flag) { - return ((header.pfFlags & flag) != 0); - } - - /** Gets the pixel format of this texture (D3DFMT_*) based on some - heuristics. Returns D3DFMT_UNKNOWN if could not recognize the - pixel format. - * @return */ - public int getPixelFormat() { - if (isCompressed()) { - return getCompressionFormat(); - } else if (isPixelFormatFlagSet(DDPF_RGB)) { - if (isPixelFormatFlagSet(DDPF_ALPHAPIXELS)) { - if (getDepth() == 32 && - header.pfRBitMask == 0x00FF0000 && - header.pfGBitMask == 0x0000FF00 && - header.pfBBitMask == 0x000000FF && - header.pfABitMask == 0xFF000000) { - return D3DFMT_A8R8G8B8; - } else if (getDepth() == 16 && - header.pfRBitMask == 0x7c00 && - header.pfGBitMask == 0x3e0 && - header.pfBBitMask == 0x1f && - header.pfABitMask == 0x8000) { - return D3DFMT_A1R5G5B5; - } - } else { - if (getDepth() == 24 && - header.pfRBitMask == 0x00FF0000 && - header.pfGBitMask == 0x0000FF00 && - header.pfBBitMask == 0x000000FF) { - return D3DFMT_R8G8B8; - } else if (getDepth() == 32 && - header.pfRBitMask == 0x00FF0000 && - header.pfGBitMask == 0x0000FF00 && - header.pfBBitMask == 0x000000FF) { - return D3DFMT_X8R8G8B8; - } - } - } - - return D3DFMT_UNKNOWN; - } - - /** - * Indicates whether this texture is cubemap - * @return true if cubemap or false otherwise - */ - public boolean isCubemap() { - return ((header.ddsCaps1 & DDSCAPS_COMPLEX) != 0) && ((header.ddsCaps2 & DDSCAPS2_CUBEMAP) != 0); - } - - /** - * Indicates whether this texture is volume texture - * @return true if cubemap or false otherwise - */ - public boolean isVolume() { - return ((header.ddsCaps1 & DDSCAPS_COMPLEX) != 0) && ((header.ddsCaps2 & DDSCAPS2_VOLUME) != 0); - } - - /** - * Indicates whether this cubemap side present - * @param side Side to test - * @return true if side present or false otherwise - */ - public boolean isCubemapSidePresent(int side) { - return isCubemap() && (header.ddsCaps2 & side) != 0; - } - - /** Indicates whether this texture is compressed. */ - public boolean isCompressed() { - return (isPixelFormatFlagSet(DDPF_FOURCC)); - } - - /** If this surface is compressed, returns the kind of compression - used (DXT1..DXT5). */ - public int getCompressionFormat() { - return header.pfFourCC; - } - - /** Width of the texture (or the top-most mipmap if mipmaps are - present) */ - public int getWidth() { - return header.width; - } - - /** Height of the texture (or the top-most mipmap if mipmaps are - present) */ - public int getHeight() { - return header.height; - } - - /** Total number of bits per pixel. Only valid if DDPF_RGB is - present. For A8R8G8B8, would be 32. */ - public int getDepth() { - return header.pfRGBBitCount; - } - - /** Number of mip maps in the texture */ - public int getNumMipMaps() { - if (!isSurfaceDescFlagSet(DDSD_MIPMAPCOUNT)) { - return 0; - } - return header.mipMapCountOrAux; - } - - /** Gets the ith mipmap data (0..getNumMipMaps() - 1) - * @param map Mipmap index - * @return Image object - */ - public ImageInfo getMipMap(int map) { - return getMipMap( 0, map ); - } - - /** - * Gets the ith mipmap data (0..getNumMipMaps() - 1) - * @param side Cubemap side or 0 for 2D texture - * @param map Mipmap index - * @return Image object - */ - public ImageInfo getMipMap(int side, int map) { - if (!isCubemap() && (side != 0)) { - throw new RuntimeException( "Illegal side for 2D texture: " + side ); - } - if (isCubemap() && !isCubemapSidePresent(side)) { - throw new RuntimeException( "Illegal side, side not present: " + side ); - } - if (getNumMipMaps() > 0 && - ((map < 0) || (map >= getNumMipMaps()))) { - throw new RuntimeException("Illegal mipmap number " + map + " (0.." + (getNumMipMaps() - 1) + ")"); - } - - // Figure out how far to seek - int seek = Header.writtenSize(); - if (isCubemap()) { - seek += sideShiftInBytes(side); - } - for (int i = 0; i < map; i++) { - seek += mipMapSizeInBytes(i); - } - buf.limit(seek + mipMapSizeInBytes(map)); - buf.position(seek); - ByteBuffer next = buf.slice(); - buf.position(0); - buf.limit(buf.capacity()); - return new ImageInfo(next, mipMapWidth(map), mipMapHeight(map), isCompressed(), getCompressionFormat()); - } - - /** Returns an array of ImageInfos corresponding to all mipmap - levels of this DDS file. - @return Mipmap image objects set - */ - public ImageInfo[] getAllMipMaps() { - return getAllMipMaps(0); - } - - /** - * Returns an array of ImageInfos corresponding to all mipmap - * levels of this DDS file. - * @param side Cubemap side or 0 for 2D texture - * @return Mipmap image objects set - */ - public ImageInfo[] getAllMipMaps( int side ) { - int numLevels = getNumMipMaps(); - if (numLevels == 0) { - numLevels = 1; - } - ImageInfo[] result = new ImageInfo[numLevels]; - for (int i = 0; i < numLevels; i++) { - result[i] = getMipMap(side, i); - } - return result; - } - - /** Converts e.g. DXT1 compression format constant (see {@link - #getCompressionFormat}) into "DXT1". - @param compressionFormat Compression format constant - @return String format code - */ - public static String getCompressionFormatName(int compressionFormat) { - StringBuffer buf = new StringBuffer(); - for (int i = 0; i < 4; i++) { - char c = (char) (compressionFormat & 0xFF); - buf.append(c); - compressionFormat = compressionFormat >> 8; - } - return buf.toString(); - } - - public void debugPrint() { - PrintStream tty = System.err; - tty.println("Compressed texture: " + isCompressed()); - if (isCompressed()) { - int fmt = getCompressionFormat(); - String name = getCompressionFormatName(fmt); - tty.println("Compression format: 0x" + Integer.toHexString(fmt) + " (" + name + ")"); - } - tty.println("Width: " + header.width + " Height: " + header.height); - tty.println("header.pitchOrLinearSize: " + header.pitchOrLinearSize); - tty.println("header.pfRBitMask: 0x" + Integer.toHexString(header.pfRBitMask)); - tty.println("header.pfGBitMask: 0x" + Integer.toHexString(header.pfGBitMask)); - tty.println("header.pfBBitMask: 0x" + Integer.toHexString(header.pfBBitMask)); - tty.println("SurfaceDesc flags:"); - boolean recognizedAny = false; - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_CAPS, "DDSD_CAPS"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_HEIGHT, "DDSD_HEIGHT"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_WIDTH, "DDSD_WIDTH"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_PITCH, "DDSD_PITCH"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_BACKBUFFERCOUNT, "DDSD_BACKBUFFERCOUNT"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_ZBUFFERBITDEPTH, "DDSD_ZBUFFERBITDEPTH"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_ALPHABITDEPTH, "DDSD_ALPHABITDEPTH"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_LPSURFACE, "DDSD_LPSURFACE"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_PIXELFORMAT, "DDSD_PIXELFORMAT"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_MIPMAPCOUNT, "DDSD_MIPMAPCOUNT"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_LINEARSIZE, "DDSD_LINEARSIZE"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_DEPTH, "DDSD_DEPTH"); - if (!recognizedAny) { - tty.println("(none)"); - } - tty.println("Raw SurfaceDesc flags: 0x" + Integer.toHexString(header.flags)); - tty.println("Pixel format flags:"); - recognizedAny = false; - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_ALPHAPIXELS, "DDPF_ALPHAPIXELS"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_ALPHA, "DDPF_ALPHA"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_FOURCC, "DDPF_FOURCC"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_PALETTEINDEXED4, "DDPF_PALETTEINDEXED4"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_PALETTEINDEXEDTO8, "DDPF_PALETTEINDEXEDTO8"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_PALETTEINDEXED8, "DDPF_PALETTEINDEXED8"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_RGB, "DDPF_RGB"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_COMPRESSED, "DDPF_COMPRESSED"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_RGBTOYUV, "DDPF_RGBTOYUV"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_YUV, "DDPF_YUV"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_ZBUFFER, "DDPF_ZBUFFER"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_PALETTEINDEXED1, "DDPF_PALETTEINDEXED1"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_PALETTEINDEXED2, "DDPF_PALETTEINDEXED2"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_ZPIXELS, "DDPF_ZPIXELS"); - if (!recognizedAny) { - tty.println("(none)"); - } - tty.println("Raw pixel format flags: 0x" + Integer.toHexString(header.pfFlags)); - tty.println("Depth: " + getDepth()); - tty.println("Number of mip maps: " + getNumMipMaps()); - int fmt = getPixelFormat(); - tty.print("Pixel format: "); - switch (fmt) { - case D3DFMT_R8G8B8: tty.println("D3DFMT_R8G8B8"); break; - case D3DFMT_A8R8G8B8: tty.println("D3DFMT_A8R8G8B8"); break; - case D3DFMT_X8R8G8B8: tty.println("D3DFMT_X8R8G8B8"); break; - case D3DFMT_DXT1: tty.println("D3DFMT_DXT1"); break; - case D3DFMT_DXT2: tty.println("D3DFMT_DXT2"); break; - case D3DFMT_DXT3: tty.println("D3DFMT_DXT3"); break; - case D3DFMT_DXT4: tty.println("D3DFMT_DXT4"); break; - case D3DFMT_DXT5: tty.println("D3DFMT_DXT5"); break; - case D3DFMT_UNKNOWN: tty.println("D3DFMT_UNKNOWN"); break; - default: tty.println("(unknown pixel format " + fmt + ")"); break; - } - } - - //---------------------------------------------------------------------- - // Internals only below this point - // - - private static final int MAGIC = 0x20534444; - - static class Header { - int size; // size of the DDSURFACEDESC structure - int flags; // determines what fields are valid - int height; // height of surface to be created - int width; // width of input surface - int pitchOrLinearSize; - int backBufferCountOrDepth; - int mipMapCountOrAux; // number of mip-map levels requested (in this context) - int alphaBitDepth; // depth of alpha buffer requested - int reserved1; // reserved - int surface; // pointer to the associated surface memory - // NOTE: following two entries are from DDCOLORKEY data structure - // Are overlaid with color for empty cubemap faces (unused in this reader) - int colorSpaceLowValue; - int colorSpaceHighValue; - int destBltColorSpaceLowValue; - int destBltColorSpaceHighValue; - int srcOverlayColorSpaceLowValue; - int srcOverlayColorSpaceHighValue; - int srcBltColorSpaceLowValue; - int srcBltColorSpaceHighValue; - // NOTE: following entries are from DDPIXELFORMAT data structure - // Are overlaid with flexible vertex format description of vertex - // buffers (unused in this reader) - int pfSize; // size of DDPIXELFORMAT structure - int pfFlags; // pixel format flags - int pfFourCC; // (FOURCC code) - // Following five entries have multiple interpretations, not just - // RGBA (but that's all we support right now) - int pfRGBBitCount; // how many bits per pixel - int pfRBitMask; // mask for red bits - int pfGBitMask; // mask for green bits - int pfBBitMask; // mask for blue bits - int pfABitMask; // mask for alpha channel - int ddsCaps1; // Texture and mip-map flags - int ddsCaps2; // Advanced capabilities including cubemap support - int ddsCapsReserved1; - int ddsCapsReserved2; - int textureStage; // stage in multitexture cascade - - void read(ByteBuffer buf) throws IOException { - int magic = buf.getInt(); - if (magic != MAGIC) { - throw new IOException("Incorrect magic number 0x" + - Integer.toHexString(magic) + - " (expected " + MAGIC + ")"); - } - - size = buf.getInt(); - flags = buf.getInt(); - height = buf.getInt(); - width = buf.getInt(); - pitchOrLinearSize = buf.getInt(); - backBufferCountOrDepth = buf.getInt(); - mipMapCountOrAux = buf.getInt(); - alphaBitDepth = buf.getInt(); - reserved1 = buf.getInt(); - surface = buf.getInt(); - colorSpaceLowValue = buf.getInt(); - colorSpaceHighValue = buf.getInt(); - destBltColorSpaceLowValue = buf.getInt(); - destBltColorSpaceHighValue = buf.getInt(); - srcOverlayColorSpaceLowValue = buf.getInt(); - srcOverlayColorSpaceHighValue = buf.getInt(); - srcBltColorSpaceLowValue = buf.getInt(); - srcBltColorSpaceHighValue = buf.getInt(); - pfSize = buf.getInt(); - pfFlags = buf.getInt(); - pfFourCC = buf.getInt(); - pfRGBBitCount = buf.getInt(); - pfRBitMask = buf.getInt(); - pfGBitMask = buf.getInt(); - pfBBitMask = buf.getInt(); - pfABitMask = buf.getInt(); - ddsCaps1 = buf.getInt(); - ddsCaps2 = buf.getInt(); - ddsCapsReserved1 = buf.getInt(); - ddsCapsReserved2 = buf.getInt(); - textureStage = buf.getInt(); - } - - // buf must be in little-endian byte order - void write(ByteBuffer buf) { - buf.putInt(MAGIC); - buf.putInt(size); - buf.putInt(flags); - buf.putInt(height); - buf.putInt(width); - buf.putInt(pitchOrLinearSize); - buf.putInt(backBufferCountOrDepth); - buf.putInt(mipMapCountOrAux); - buf.putInt(alphaBitDepth); - buf.putInt(reserved1); - buf.putInt(surface); - buf.putInt(colorSpaceLowValue); - buf.putInt(colorSpaceHighValue); - buf.putInt(destBltColorSpaceLowValue); - buf.putInt(destBltColorSpaceHighValue); - buf.putInt(srcOverlayColorSpaceLowValue); - buf.putInt(srcOverlayColorSpaceHighValue); - buf.putInt(srcBltColorSpaceLowValue); - buf.putInt(srcBltColorSpaceHighValue); - buf.putInt(pfSize); - buf.putInt(pfFlags); - buf.putInt(pfFourCC); - buf.putInt(pfRGBBitCount); - buf.putInt(pfRBitMask); - buf.putInt(pfGBitMask); - buf.putInt(pfBBitMask); - buf.putInt(pfABitMask); - buf.putInt(ddsCaps1); - buf.putInt(ddsCaps2); - buf.putInt(ddsCapsReserved1); - buf.putInt(ddsCapsReserved2); - buf.putInt(textureStage); - } - - private static int size() { - return 124; - } - - private static int pfSize() { - return 32; - } - - private static int writtenSize() { - return 128; - } - } - - private DDSImage() { - } - - private void readFromFile(File file) throws IOException { - fis = new FileInputStream(file); - chan = fis.getChannel(); - ByteBuffer buf = chan.map(FileChannel.MapMode.READ_ONLY, - 0, (int) file.length()); - readFromBuffer(buf); - } - - private void readFromBuffer(ByteBuffer buf) throws IOException { - this.buf = buf; - buf.order(ByteOrder.LITTLE_ENDIAN); - header = new Header(); - header.read(buf); - fixupHeader(); - } - - private void initFromData(int d3dFormat, - int width, - int height, - ByteBuffer[] mipmapData) throws IllegalArgumentException { - // Check size of mipmap data compared against format, width and - // height - int topmostMipmapSize = width * height; - int pitchOrLinearSize = width; - boolean isCompressed = false; - switch (d3dFormat) { - case D3DFMT_R8G8B8: topmostMipmapSize *= 3; pitchOrLinearSize *= 3; break; - case D3DFMT_A8R8G8B8: topmostMipmapSize *= 4; pitchOrLinearSize *= 4; break; - case D3DFMT_X8R8G8B8: topmostMipmapSize *= 4; pitchOrLinearSize *= 4; break; - case D3DFMT_DXT1: - case D3DFMT_DXT2: - case D3DFMT_DXT3: - case D3DFMT_DXT4: - case D3DFMT_DXT5: - topmostMipmapSize = computeCompressedBlockSize(width, height, 1, d3dFormat); - pitchOrLinearSize = topmostMipmapSize; - isCompressed = true; - break; - default: - throw new IllegalArgumentException("d3dFormat must be one of the known formats"); - } - - // Now check the mipmaps against this size - int curSize = topmostMipmapSize; - int mipmapWidth = width; - int mipmapHeight = height; - int totalSize = 0; - for (int i = 0; i < mipmapData.length; i++) { - if (mipmapData[i].remaining() != curSize) { - throw new IllegalArgumentException("Mipmap level " + i + - " didn't match expected data size (expected " + curSize + ", got " + - mipmapData[i].remaining() + ")"); - } - /* Change Daniel Senff - * I got the problem, that MipMaps below the dimension of 8x8 blocks with DXT5 - * where assume smaller than they are created. - * Assumed: < 16byte where 16byte where used by the compression. */ - if(isCompressed) { - // size calculation for compressed mipmaps - if(mipmapWidth > 1) mipmapWidth /= 2; - if(mipmapHeight > 1) mipmapHeight /= 2; - curSize = computeCompressedBlockSize(mipmapWidth, mipmapHeight, 1, d3dFormat); - } else { - curSize /= 4; - } - totalSize += mipmapData[i].remaining(); - } - - // OK, create one large ByteBuffer to hold all of the mipmap data - totalSize += Header.writtenSize(); - ByteBuffer buf = ByteBuffer.allocate(totalSize); - buf.position(Header.writtenSize()); - for (int i = 0; i < mipmapData.length; i++) { - buf.put(mipmapData[i]); - } - this.buf = buf; - - // Allocate and initialize a Header - header = new Header(); - header.size = Header.size(); - header.flags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; - if (mipmapData.length > 1) { - header.flags |= DDSD_MIPMAPCOUNT; - header.mipMapCountOrAux = mipmapData.length; - } - header.width = width; - header.height = height; - if (isCompressed) { - header.flags |= DDSD_LINEARSIZE; - header.pfFlags |= DDPF_FOURCC; - header.pfFourCC = d3dFormat; - } else { - header.flags |= DDSD_PITCH; - // Figure out the various settings from the pixel format - header.pfFlags |= DDPF_RGB; - switch (d3dFormat) { - case D3DFMT_R8G8B8: header.pfRGBBitCount = 24; break; - case D3DFMT_A8R8G8B8: header.pfRGBBitCount = 32; header.pfFlags |= DDPF_ALPHAPIXELS; break; - case D3DFMT_X8R8G8B8: header.pfRGBBitCount = 32; break; - } - header.pfRBitMask = 0x00FF0000; - header.pfGBitMask = 0x0000FF00; - header.pfBBitMask = 0x000000FF; - if (d3dFormat == D3DFMT_A8R8G8B8) { - header.pfABitMask = 0xFF000000; - } - } - header.pitchOrLinearSize = pitchOrLinearSize; - header.pfSize = Header.pfSize(); - // Not sure whether we can get away with leaving the rest of the - // header blank - } - - // Microsoft doesn't follow their own specifications and the - // simplest conversion using the DxTex tool to e.g. a DXT3 texture - // results in an illegal .dds file without either DDSD_PITCH or - // DDSD_LINEARSIZE set in the header's flags. This code, adapted - // from the DevIL library, fixes up the header in these situations. - private void fixupHeader() { - if (isCompressed() && !isSurfaceDescFlagSet(DDSD_LINEARSIZE)) { - // Figure out how big the linear size should be - int depth = header.backBufferCountOrDepth; - if (depth == 0) { - depth = 1; - } - - header.pitchOrLinearSize = computeCompressedBlockSize(getWidth(), getHeight(), depth, getCompressionFormat()); - header.flags |= DDSD_LINEARSIZE; - } - } - - private static int computeCompressedBlockSize(int width, - int height, - int depth, - int compressionFormat) { - int blockSize = ((width + 3)/4) * ((height + 3)/4) * ((depth + 3)/4); - switch (compressionFormat) { - case D3DFMT_DXT1: blockSize *= 8; break; - default: blockSize *= 16; break; - } - return blockSize; - } - - private int mipMapWidth(int map) { - int width = getWidth(); - for (int i = 0; i < map; i++) { - width >>= 1; - } - return Math.max(width, 1); - } - - private int mipMapHeight(int map) { - int height = getHeight(); - for (int i = 0; i < map; i++) { - height >>= 1; - } - return Math.max(height, 1); - } - - public int mipMapSizeInBytes(int map) { - int width = mipMapWidth(map); - int height = mipMapHeight(map); - if (isCompressed()) { - int blockSize = (getCompressionFormat() == D3DFMT_DXT1 ? 8 : 16); - return ((width+3)/4)*((height+3)/4)*blockSize; - } else { - return width * height * (getDepth() / 8); - } - } - - private int sideSizeInBytes() { - int numLevels = getNumMipMaps(); - if (numLevels == 0) { - numLevels = 1; - } - - int size = 0; - for (int i = 0; i < numLevels; i++) { - size += mipMapSizeInBytes(i); - } - - return size; - } - - private int sideShiftInBytes(int side) { - int[] sides = { - DDSCAPS2_CUBEMAP_POSITIVEX, - DDSCAPS2_CUBEMAP_NEGATIVEX, - DDSCAPS2_CUBEMAP_POSITIVEY, - DDSCAPS2_CUBEMAP_NEGATIVEY, - DDSCAPS2_CUBEMAP_POSITIVEZ, - DDSCAPS2_CUBEMAP_NEGATIVEZ - }; - - int shift = 0; - int sideSize = sideSizeInBytes(); - for (int i = 0; i < sides.length; i++) { - int temp = sides[i]; - if ((temp & side) != 0) { - return shift; - } - - shift += sideSize; - } - - throw new RuntimeException("Illegal side: " + side); - } - - private boolean printIfRecognized(PrintStream tty, int flags, int flag, String what) { - if ((flags & flag) != 0) { - tty.println(what); - return true; - } - return false; - } -} diff --git a/src/ddsutils/jogl/TEXImage.java b/src/ddsutils/jogl/TEXImage.java deleted file mode 100644 index 1cb5d51..0000000 --- a/src/ddsutils/jogl/TEXImage.java +++ /dev/null @@ -1,528 +0,0 @@ -/* - * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. - */ - -package jogl; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintStream; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.channels.FileChannel; -import java.util.Collection; -import java.util.Vector; - -import jogl.TEXImage.Header.EmbeddedBuffer; - - -/** A reader and writer for Tex (.tex) files, which are - used to describe textures. These files can contain multiple mipmap - levels in one file. This class is currently minimal and does not - support all of the possible file formats. - http://www.realgpx.com/partage/texformat.htm*/ - -public class TEXImage { - - - - private FileInputStream fis; - private FileChannel chan; - private ByteBuffer buf; - private Header header; - private Vector embeddedMap; - - - - // Known pixel formats - public static final int D3DFMT_UNKNOWN = 0; - public static final int D3DFMT_R8G8B8 = 20; - public static final int D3DFMT_A8R8G8B8 = 21; - public static final int D3DFMT_X8R8G8B8 = 22; - // The following are also valid FourCC codes - public static final int D3DFMT_DXT1 = 0x31545844; - public static final int D3DFMT_DXT2 = 0x32545844; - public static final int D3DFMT_DXT3 = 0x33545844; - public static final int D3DFMT_DXT4 = 0x34545844; - public static final int D3DFMT_DXT5 = 0x35545844; - - /** Reads a DirectDraw surface from the specified file name, - returning the resulting DDSImage. - - @param filename File name - @return DDS image object - @throws java.io.IOException if an I/O exception occurred - */ - public static TEXImage read(String filename) throws IOException { - return read(new File(filename)); - } - - /** Reads a DirectDraw surface from the specified file, returning - the resulting DDSImage. - - @param file File object - @return DDS image object - @throws java.io.IOException if an I/O exception occurred - */ - public static TEXImage read(File file) throws IOException { - TEXImage image = new TEXImage(); - image.readFromFile(file); - return image; - } - - /** Reads a DirectDraw surface from the specified ByteBuffer, returning - the resulting DDSImage. - - @param buf Input data - @return DDS image object - @throws java.io.IOException if an I/O exception occurred - */ - public static TEXImage read(ByteBuffer buf) throws IOException { - TEXImage image = new TEXImage(); - image.readFromBuffer(buf); - return image; - } - - /** Closes open files and resources associated with the open - DDSImage. No other methods may be called on this object once - this is called. */ - public void close() { - try { - if (chan != null) { - chan.close(); - chan = null; - } - if (fis != null) { - fis.close(); - fis = null; - } - buf = null; - } catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * Creates a new DDSImage from data supplied by the user. The - * resulting DDSImage can be written to disk using the write() - * method. - * - * @param d3dFormat the D3DFMT_ constant describing the data; it is - * assumed that it is packed tightly - * @param width the width in pixels of the topmost mipmap image - * @param height the height in pixels of the topmost mipmap image - * @param mipmapData the data for each mipmap level of the resulting - * DDSImage; either only one mipmap level should - * be specified, or they all must be - * @throws IllegalArgumentException if the data does not match the - * specified arguments - * @return DDS image object - */ - public static TEXImage createFromData(int d3dFormat, - int width, - int height, - ByteBuffer[] mipmapData) throws IllegalArgumentException { - TEXImage image = new TEXImage(); - image.initFromData(d3dFormat, width, height, mipmapData); - return image; - } - - /** Determines from the magic number whether the given InputStream - points to a DDS image. The given InputStream must return true - from markSupported() and support a minimum of four bytes of - read-ahead. - - @param in Stream to check - @return true if input stream is DDS image or false otherwise - @throws java.io.IOException if an I/O exception occurred - */ - public static boolean isTEXImage(InputStream in) throws IOException { - if (!(in instanceof BufferedInputStream)) { - in = new BufferedInputStream(in); - } - if (!in.markSupported()) { - throw new IOException("Can not test non-destructively whether given InputStream is a TEX image"); - } - in.mark(4); - int magic = 0; - for (int i = 0; i < 4; i++) { - int tmp = in.read(); - if (tmp < 0) { - in.reset(); - return false; - } - magic = ((magic >>> 8) | (tmp << 24)); - } - in.reset(); - return (magic == MAGIC); - } - - /** - * Writes this DDSImage to the specified file name. - * @param filename File name to write to - * @throws java.io.IOException if an I/O exception occurred - */ - public void write(String filename) throws IOException { - write(new File(filename)); - } - - /** - * Writes this TEXImage to the specified file name. - * @param file File object to write to - * @throws java.io.IOException if an I/O exception occurred - */ - public void write(File file) throws IOException { - FileOutputStream stream = new FileOutputStream(file); - FileChannel chan = stream.getChannel(); - // Create ByteBuffer for header in case the start of our - // ByteBuffer isn't actually memory-mapped - ByteBuffer hdr = ByteBuffer.allocate(Header.writtenSize()); - hdr.order(ByteOrder.LITTLE_ENDIAN); - header.write(hdr); - hdr.rewind(); - chan.write(hdr); - buf.position(Header.writtenSize()); - chan.write(buf); - chan.force(true); - chan.close(); - stream.close(); - } - - - /** Gets the pixel format of this texture (D3DFMT_*) based on some - heuristics. Returns D3DFMT_UNKNOWN if could not recognize the - pixel format. */ - public int getPixelFormat() { - return embeddedMap.get(0).getPixelFormat(); - } - - - /** Indicates whether this texture is compressed. */ - public boolean isCompressed() { - return embeddedMap.get(0).isCompressed(); - } - - /** If this surface is compressed, returns the kind of compression - used (DXT1..DXT5). */ - public int getCompressionFormat() { - return embeddedMap.get(0).getCompressionFormat(); - } - - /** Width of the texture (or the top-most mipmap if mipmaps are - present) */ - public int getWidth() { - return header.width; - } - - /** Height of the texture (or the top-most mipmap if mipmaps are - present) */ - public int getHeight() { - return header.height; - } - - /** Number of mip maps in the texture */ - public int getNumMipMaps() { - return header.mipMapCountOrAux; - } - - - /** Converts e.g. DXT1 compression format constant (see {@link - #getCompressionFormat}) into "DXT1". - @param compressionFormat Compression format constant - @return String format code - */ - public static String getCompressionFormatName(int compressionFormat) { - StringBuffer buf = new StringBuffer(); - for (int i = 0; i < 4; i++) { - char c = (char) (compressionFormat & 0xFF); - buf.append(c); - compressionFormat = compressionFormat >> 8; - } - return buf.toString(); - } - - public void debugPrint() { - /*PrintStream tty = System.err; - tty.println("Compressed texture: " + isCompressed()); - if (isCompressed()) { - int fmt = getCompressionFormat(); - String name = getCompressionFormatName(fmt); - tty.println("Compression format: 0x" + Integer.toHexString(fmt) + " (" + name + ")"); - } - tty.println("Width: " + header.width + " Height: " + header.height); - tty.println("header.pitchOrLinearSize: " + header.pitchOrLinearSize); - tty.println("header.pfRBitMask: 0x" + Integer.toHexString(header.pfRBitMask)); - tty.println("header.pfGBitMask: 0x" + Integer.toHexString(header.pfGBitMask)); - tty.println("header.pfBBitMask: 0x" + Integer.toHexString(header.pfBBitMask)); - tty.println("SurfaceDesc flags:"); - boolean recognizedAny = false; - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_CAPS, "DDSD_CAPS"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_HEIGHT, "DDSD_HEIGHT"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_WIDTH, "DDSD_WIDTH"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_PITCH, "DDSD_PITCH"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_BACKBUFFERCOUNT, "DDSD_BACKBUFFERCOUNT"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_ZBUFFERBITDEPTH, "DDSD_ZBUFFERBITDEPTH"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_ALPHABITDEPTH, "DDSD_ALPHABITDEPTH"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_LPSURFACE, "DDSD_LPSURFACE"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_PIXELFORMAT, "DDSD_PIXELFORMAT"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_MIPMAPCOUNT, "DDSD_MIPMAPCOUNT"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_LINEARSIZE, "DDSD_LINEARSIZE"); - recognizedAny |= printIfRecognized(tty, header.flags, DDSD_DEPTH, "DDSD_DEPTH"); - if (!recognizedAny) { - tty.println("(none)"); - } - tty.println("Raw SurfaceDesc flags: 0x" + Integer.toHexString(header.flags)); - tty.println("Pixel format flags:"); - recognizedAny = false; - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_ALPHAPIXELS, "DDPF_ALPHAPIXELS"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_ALPHA, "DDPF_ALPHA"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_FOURCC, "DDPF_FOURCC"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_PALETTEINDEXED4, "DDPF_PALETTEINDEXED4"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_PALETTEINDEXEDTO8, "DDPF_PALETTEINDEXEDTO8"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_PALETTEINDEXED8, "DDPF_PALETTEINDEXED8"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_RGB, "DDPF_RGB"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_COMPRESSED, "DDPF_COMPRESSED"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_RGBTOYUV, "DDPF_RGBTOYUV"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_YUV, "DDPF_YUV"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_ZBUFFER, "DDPF_ZBUFFER"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_PALETTEINDEXED1, "DDPF_PALETTEINDEXED1"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_PALETTEINDEXED2, "DDPF_PALETTEINDEXED2"); - recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_ZPIXELS, "DDPF_ZPIXELS"); - if (!recognizedAny) { - tty.println("(none)"); - } - tty.println("Raw pixel format flags: 0x" + Integer.toHexString(header.pfFlags)); - tty.println("Depth: " + getDepth()); - tty.println("Number of mip maps: " + getNumMipMaps()); - int fmt = getPixelFormat(); - tty.print("Pixel format: "); - switch (fmt) { - case D3DFMT_R8G8B8: tty.println("D3DFMT_R8G8B8"); break; - case D3DFMT_A8R8G8B8: tty.println("D3DFMT_A8R8G8B8"); break; - case D3DFMT_X8R8G8B8: tty.println("D3DFMT_X8R8G8B8"); break; - case D3DFMT_DXT1: tty.println("D3DFMT_DXT1"); break; - case D3DFMT_DXT2: tty.println("D3DFMT_DXT2"); break; - case D3DFMT_DXT3: tty.println("D3DFMT_DXT3"); break; - case D3DFMT_DXT4: tty.println("D3DFMT_DXT4"); break; - case D3DFMT_DXT5: tty.println("D3DFMT_DXT5"); break; - case D3DFMT_UNKNOWN: tty.println("D3DFMT_UNKNOWN"); break; - default: tty.println("(unknown pixel format " + fmt + ")"); break; - }*/ - } - - //---------------------------------------------------------------------- - // Internals only below this point - // - - // private static final int MAGIC = 0x20534444; - private static final int MAGIC = 0x54455800; - - static class Header { - int height; // height of surface to be created - int width; // width of input surface - int mipMapCountOrAux; // number of mip-map levels requested (in this context), range of 1 to 8 - int alphaBitDepth; // depth of alpha buffer requested - Vector embeddedMap; - - void read(ByteBuffer buf) throws IOException { - int magic = buf.getInt(); - if (magic != MAGIC) { - throw new IOException("Incorrect magic number 0x" + - Integer.toHexString(magic) + - " (expected " + MAGIC + ")"); - } - - width = buf.getInt(); - height = buf.getInt(); - alphaBitDepth = buf.getInt(); - mipMapCountOrAux = buf.getInt(); - - embeddedMap = readHeaderTable(buf); // header data for embedded dds - -// for (EmbeddedBuffer embBuffer : embeddedMap) { -// DDSImage image = DDSImage.read(embBuffer.buffer); -// } - } - - private Vector readHeaderTable(ByteBuffer buf) { - int offset, size, currentPos; - Vector embeddedBuffer = new Vector(); - /* - * iterate over 5 tables, mapping pixelformat - * Table #1 : r8g8b8, x8r8g8b8, r5g6b5, x1r5g5b5 - * Table #2 : a8r8g8b8, a4r4g4b4, DXT2, DXT3, DXT4 - * Table #3 : a1r5g5b5 - * Table #4 : DXT1 - * Table #5 : DXT5 - */ - for (int t = 0; t < 5; t++) { - // iterate over 8 maps -// System.out.println("Table: "+t); - for (int i = 0; i < 8; i++) { - offset = buf.getInt(); - size = buf.getInt(); -// System.out.println("offset="+offset+" size="+size); - EmbeddedBuffer embBuffer = new EmbeddedBuffer(); - if((offset != -1) && (size != -1)) { // if not blank - currentPos = buf.position(); - byte[] ddsbuffer = new byte[size]; - buf.position(offset); - buf.get(ddsbuffer); - embBuffer.buffer = ByteBuffer.wrap(ddsbuffer); - embBuffer.size = size; - embBuffer.offset = offset; - embeddedBuffer.add(embBuffer); - buf.position(currentPos); - } - } - } - for (EmbeddedBuffer embeddedBuffer2 : embeddedBuffer) { - embeddedBuffer2.buffer.rewind(); - embeddedBuffer2.buffer.order(ByteOrder.LITTLE_ENDIAN); - } - - return embeddedBuffer; - } - - class EmbeddedBuffer { - ByteBuffer buffer; - int size; - int offset; - } - - // buf must be in little-endian byte order - void write(ByteBuffer buf) { - buf.putInt(MAGIC); - buf.putInt(width); - buf.putInt(height); - buf.putInt(alphaBitDepth); - buf.putInt(mipMapCountOrAux); - - // header table - - - // embedded buffer - - } - - private static int size() { - return 340; - } - - private static int pfSize() { - return 32; - } - - private static int writtenSize() { - return 340; - } - } - - private TEXImage() { - embeddedMap = new Vector(); - } - - private void readFromFile(File file) throws IOException { - fis = new FileInputStream(file); - chan = fis.getChannel(); - ByteBuffer buf = chan.map(FileChannel.MapMode.READ_ONLY, - 0, (int) file.length()); - readFromBuffer(buf); - } - - private void readFromBuffer(ByteBuffer buf) throws IOException { - this.buf = buf; - buf.order(ByteOrder.LITTLE_ENDIAN); - header = new Header(); - header.read(buf); - for (EmbeddedBuffer embBuffer : header.embeddedMap) { - embBuffer.buffer.rewind(); - embeddedMap.add(DDSImage.read(embBuffer.buffer)); - } - } - - private void initFromData(int d3dFormat, - int width, - int height, - ByteBuffer[] mipmapData) throws IllegalArgumentException { - // Check size of mipmap data compared against format, width and - // height - int topmostMipmapSize = width * height; - boolean isCompressed = false; - - for (int i = 0; i < mipmapData.length; i++) { - DDSImage image = DDSImage.createFromData(d3dFormat, width, height, mipmapData); - } - - - - - // Allocate and initialize a Header - header = new Header(); - if (mipmapData.length > 1) { - header.mipMapCountOrAux = mipmapData.length; - } - header.width = width; - header.height = height; - } - - /** - * Get a Vector of all DDSImages embedded in this TEX-file. - * @return - */ - public Vector getAllEmbeddedMaps() { - return embeddedMap; - } - - /** - * Get a {@link DDSImage} embedded in this TEX-file. - * @param index - * @return - */ - public DDSImage getEmbeddedMaps(int index) { - return embeddedMap.get(index); - } - - public int getDepth() { - return embeddedMap.get(0).getDepth(); - } -} diff --git a/src/ddsutils/jogl/ddsimage-class-bug b/src/ddsutils/jogl/ddsimage-class-bug deleted file mode 100644 index 0d7c34a..0000000 --- a/src/ddsutils/jogl/ddsimage-class-bug +++ /dev/null @@ -1,77 +0,0 @@ -DDSImage-Class - Bug with compressed mipmaps - -Hello everybody! - -I've been working on some DirectDraw-Surface tools. For now, mostly simple stuff like texture loading, preview and MipMap generating, resaving. I used the DDSImage-class from JOGL as base for the File-Handling and using JSquish for DXT-(De)Compression. - -When I implemented automatic generated MipMaps I stumbled upon a possible bug in the DDSImage class. I hope this is the right place to address this, the bugtracker seemed kinda dead. - -Here is my scenario: -I generate an array of ByteBuffers, which contain the pixel data. Each ByteBuffer is then compressed using a DXTn-method. This data-array is then given to the DDSImage class to create a new DDSImage-Object, which then can be written to disc. - -DDSImage.createFromData(pixelformat, width, height, mipmapBufferArray); - -This gave me an IllegalArgumentException in the initFromData()-method saying, that the remaining data size for the lowest mipmaps exceeded the expected size. In fact the compressed buffer could never be smaller than 16kbyte (8kbyte DXT1). - -I did some research and found, that DXT-compressors always use the usual 8x8 pixel block for compression, even if the actual texture is smaller. So the resulting compressed block is always of this size. -This is even hinted at the official DDS-Specification at MSDN - -So now what is the problem. The current implementation of the expected mipmap data size: - - -// Now check the mipmaps against this size -int curSize = topmostMipmapSize; -int totalSize = 0; -for (int i = 0; i < mipmapData.length; i++) { - if (mipmapData[i].remaining() != curSize) { - throw new IllegalArgumentException("Mipmap level " + i + - " didn't match expected data size (expected " + curSize + ", got " + - mipmapData[i].remaining() + ")"); - } - curSize /= 4; - totalSize += mipmapData[i].remaining(); -} - -This always expects the datasize is 1/4 the size of the mipmap before. Works as far as MipMaps are bigger or equal 8x8 pixels. In that case it throughs my mentioned exception, as it expects the size to be smaller than it actually is. - -I did some patch job to fix this. - -// Now check the mipmaps against this size -int curSize = topmostMipmapSize; -int mipmapWidth = width; -int mipmapHeight = height; -int totalSize = 0; -for (int i = 0; i < mipmapData.length; i++) { - if (mipmapData[i].remaining() != curSize) { - throw new IllegalArgumentException("Mipmap level " + i + - " didn't match expected data size (expected " + curSize + ", got " + - mipmapData[i].remaining() + ")"); - } - /* Change Daniel Senff - * I got the problem, that MipMaps below the dimension of 8x8 blocks with DXT5 - * where assume smaller than they are created. - * Assumed: < 16byte where 16byte where used by the compression. */ - if(isCompressed) { - // size calculation for compressed mipmaps - mipmapHeight /= 2; - mipmapWidth /= 2; - curSize = computeCompressedBlockSize(mipmapWidth, mipmapHeight, 1, d3dFormat); - } else { - curSize /= 4; - } - /* changes end */ - totalSize += mipmapData[i].remaining(); -} - - - -This patchjob is tested for saving DXT1-5 compressed files. -I bet it could be done a bit nicer, but it does the job. - -I don't know if this can be classified as a bug, but I certainly thing, the original behavour was not desired and could be fixed in future versions. - -So much from my part, I'd like to get some feedback on how to patch this a bit nicer. :) - - -best regards, -Dahie diff --git a/src/ddsutils/model/AbstractTextureImage.java b/src/ddsutils/model/AbstractTextureImage.java deleted file mode 100644 index 0905bd7..0000000 --- a/src/ddsutils/model/AbstractTextureImage.java +++ /dev/null @@ -1,234 +0,0 @@ -package model; - -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; - -import jogl.DDSImage; -import ddsutil.DDSUtil; -import ddsutil.NonCubicDimensionException; -import ddsutil.PixelFormats; - - -public abstract class AbstractTextureImage implements TextureImage { - - protected int height; - protected int width; - protected int pixelformat; - protected File file = null; - protected boolean hasMipMaps = false; - protected int numMipMaps = 0; - protected int depth; - - /** - * MipMap at the highest Level, ie the original - */ - protected MipMaps mipMaps = new MipMaps(); - - /** - * Depth of color for all channels - * @return int - */ - @Override - public int getDepth() { - return depth; - } - - /** - * Depth of color of each channel - * @return int - */ - @Override - public int getChannelDepth() { - - switch(this.pixelformat){ - case DDSImage.D3DFMT_A8R8G8B8: - case DDSImage.D3DFMT_X8R8G8B8: - case DDSImage.D3DFMT_DXT5: - case DDSImage.D3DFMT_DXT3: - case DDSImage.D3DFMT_DXT2: - case DDSImage.D3DFMT_DXT4: - return depth/4; - case DDSImage.D3DFMT_DXT1: - case DDSImage.D3DFMT_R8G8B8: - return depth/3; - } - return 0; - } - - /** - * Returns the absolute path to the {@link File}. - * @return - */ - @Override - public String getAbsolutePath() { - return this.file.getAbsolutePath(); - } - - /** - * Returns the name of the {@link File}. - * @return - */ - private String getFileName() { - return this.file.getName(); - } - - /** - * Returns the associated {@link File} - * @return File - */ - @Override - public File getFile() { - return this.file; - } - - /** - * Width of the topmost MipMap - * @return - */ - @Override - public int getHeight() { - return this.height; - } - - /** - * Height of the topmost MipMap - * @return - */ - @Override - public int getWidth() { - return this.width; - } - - - /** - * Get the Format in which pixel are stored in the file as internal stored Integer-value. - * @return in - */ - @Override - public int getPixelformat() { - return this.pixelformat; - } - - /** - * Sets the format in which pixel are stored in the file. - * @param pixelformat - */ - @Override - public void setPixelformat(final int pixelformat) { - this.pixelformat = pixelformat; - } - - /** - * Sets the format in which pixel are stored in the file. - * @param pixelformat - */ - @Override - public void setPixelformat(final PixelFormat pixelformat) { - this.setPixelformat(PixelFormats.convertPixelformat(pixelformat)); - } - - - /** - * Returns whether or not the dds-file has MipMaps. - * Usually only textures whose size is a power of two may have mipmaps. - * @return boolean - */ - @Override - public boolean hasMipMaps() { - return this.hasMipMaps; - } - - /** - * Returns the number of MipMaps in this file. - * @return int Number of MipMaps - */ - @Override - public int getNumMipMaps() { - return numMipMaps; - } - - - @Override - public void write() throws IOException { - this.write(this.file); - } - - /** - * Returns true if the dds-file is compressed as DXT1-5 - * @return boolean - */ - @Override - public boolean isCompressed() { - return PixelFormats.isDXTCompressed(pixelformat); - } - - /** - * Gets the format in which pixels are stored as a verbose {@link String}. - * @return - */ - @Override - public String getPixelformatVerbose() { - return PixelFormats.verbosePixelformat(this.pixelformat); - } - - /** - * Activates the generation of MipMaps when saving the Texture to disk. - * @param generateMipMaps - * @throws IllegalArgumentException - */ - @Override - public void setHasMipMaps(final boolean generateMipMaps) throws IllegalArgumentException{ - if(isPowerOfTwo(getTopMipMap().getWidth()) && isPowerOfTwo(getTopMipMap().getHeight())) - this.hasMipMaps = generateMipMaps; - else throw new NonCubicDimensionException(); - } - - /** - * Sets a new {@link BufferedImage} as the Topmost MipMap and generates new MipMaps accordingly. - * @param bi - */ - @Override - public void setData(final BufferedImage bi) { - this.width = bi.getWidth(); - this.height = bi.getHeight(); - this.setTopMipMap(bi); - } - - /** - * Sets the topmost MipMap. - * @param bi - */ - public void setTopMipMap(BufferedImage bi) { - this.mipMaps.setMipMap(TOP_MOST_MIP_MAP, bi); - } - - /** - * Returns the top-most MipMap. - * @return - */ - public BufferedImage getTopMipMap() { - return this.mipMaps.getMipMap(TOP_MOST_MIP_MAP); - } - - /** - * Returns the topmost MipMap - * @return {@link BufferedImage} - */ - @Override - public BufferedImage getData() { - // FIXME shouldn't this be getImage? - return this.getTopMipMap(); - } - - /** - * Checks if a value is a power of two - * @param value - * @return - */ - public static boolean isPowerOfTwo(final int value) { - double p = Math.floor(Math.log(value) / Math.log(2.0)); - double n = Math.pow(2.0, p); - return (n==value); - } -} diff --git a/src/ddsutils/model/AbstractTextureMap.java b/src/ddsutils/model/AbstractTextureMap.java deleted file mode 100644 index e24b8f1..0000000 --- a/src/ddsutils/model/AbstractTextureMap.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * - */ -package model; - -import gr.zdimensions.jsquish.Squish; -import gr.zdimensions.jsquish.Squish.CompressionType; - -import java.awt.image.BufferedImage; -import java.nio.ByteBuffer; - -import javax.activation.UnsupportedDataTypeException; - -import compression.DXTBufferCompressor; - -import ddsutil.PixelFormats; - - - -/** - * Abstract TextureMap - * @author danielsenff - * - */ -public abstract class AbstractTextureMap implements TextureMap { - - public AbstractTextureMap() {} - - @Override - public ByteBuffer[] getDXTCompressedBuffer(final int pixelformat) - throws UnsupportedDataTypeException { - CompressionType compressionType = PixelFormats.getSquishCompressionFormat(pixelformat); - return this.getDXTCompressedBuffer(compressionType ); - } - - /** - * @param bi - * @param compressionType - * @return - */ - @Override - public ByteBuffer compress(final BufferedImage bi, - final Squish.CompressionType compressionType) { - DXTBufferCompressor compi = new DXTBufferCompressor(bi, compressionType); - return compi.getByteBuffer(); - } - -} diff --git a/src/ddsutils/model/DDSCubeImageFile.java b/src/ddsutils/model/DDSCubeImageFile.java deleted file mode 100644 index 03229bf..0000000 --- a/src/ddsutils/model/DDSCubeImageFile.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * - */ -package model; - -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; - -import jogl.DDSImage; - - -/** - * @author danielsenff - * - */ -public class DDSCubeImageFile extends DDSFile { - - /** - * 0 - top - * 1 - bottom - * 2 - front - * 3 - back - * 4 - left - * 5 - right - */ - private BufferedImage[] cubeFaces; - - /** - * Faces of the Cube - * - */ - public enum Faces { - top, bottom, front, back, left, right - } - - /** - * @param filename - * @throws IOException - */ - public DDSCubeImageFile(String filename) throws IOException { - super(filename); - this.cubeFaces = new BufferedImage[6]; - } - - /** - * @param file - * @throws IOException - * @throws IOException - */ - public DDSCubeImageFile(File file) throws IOException { - super(file); - - } - - /** - * @param filename - * @param bi - * @param pixelformat - * @param hasmipmaps - * @throws IOException - */ - public DDSCubeImageFile(File file, BufferedImage bi, int pixelformat, - boolean hasmipmaps) { - super(file, bi, pixelformat, hasmipmaps); - } - - /** - * @param file - * @param ddsimage - */ - public DDSCubeImageFile(File file, DDSImage ddsimage) { - super(file, ddsimage); - // TODO Auto-generated constructor stub - } - - /** - * @param filename - * @param ddsimage - */ - public DDSCubeImageFile(String filename, DDSImage ddsimage) { - super(filename, ddsimage); - } - - -} diff --git a/src/ddsutils/model/DDSFile.java b/src/ddsutils/model/DDSFile.java deleted file mode 100644 index 6ed1838..0000000 --- a/src/ddsutils/model/DDSFile.java +++ /dev/null @@ -1,255 +0,0 @@ -package model; - -/** - * - */ - -import gr.zdimensions.jsquish.Squish.CompressionType; - -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.List; - -import javax.activation.UnsupportedDataTypeException; - -import jogl.DDSImage; - -import compression.ARGBBufferDecompressor; -import compression.BufferDecompressor; -import compression.DXTBufferDecompressor; - -import ddsutil.MipMapsUtil; -import ddsutil.PixelFormats; - - -/** - * @author danielsenff - * - */ -public class DDSFile extends AbstractTextureImage{ - - protected TextureType textureType; - private DDSImage ddsimage; - - /** - * @param filename - */ - public DDSFile(final String filename) { - this(new File(filename)); - } - - /** - * Constructs a DDSFile from a {@link File} - * @param file - */ - public DDSFile(final File file) { - this.file = file; - try { - init(DDSImage.read(file)); - } catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * Constructs a DDSFile from a {@link File} and a {@link DDSImage} - * @param file - * @param ddsimage - */ - public DDSFile(final File file, final DDSImage ddsimage) { - this.file = file; - init(ddsimage); - } - - /** - * Constructs a DDSFile from a file path and a {@link DDSImage} - * @param filename - * @param ddsimage - */ - public DDSFile(final String filename, final DDSImage ddsimage) { - this(new File(filename), ddsimage); - } - - /** - * - * @param filename - * @param bi - * @param pixelformat - * @param hasMipMaps - */ - public DDSFile(final File filename, - final BufferedImage bi, - final int pixelformat, - final boolean hasMipMaps) { - this.file = filename; - - this.width = bi.getWidth(); - this.height = bi.getHeight(); - this.pixelformat = pixelformat; - this.hasMipMaps = hasMipMaps; - if(hasMipMaps) { - this.numMipMaps = MipMapsUtil.calculateMaxNumberOfMipMaps(width, height); - } else { - this.numMipMaps = 1; - } - this.mipMaps = new MipMaps(this.numMipMaps); - this.mipMaps.setMipMap(0, bi); - } - - /** - * @param ddsimage - */ - protected void init(final DDSImage ddsimage) { - this.ddsimage = ddsimage; - this.width = ddsimage.getWidth(); - this.height = ddsimage.getHeight(); - this.depth = ddsimage.getDepth(); - this.pixelformat = ddsimage.getPixelFormat(); - this.textureType = getTextureType(ddsimage); - this.numMipMaps = ddsimage.getNumMipMaps(); - this.mipMaps = new MipMaps(this.numMipMaps); - this.hasMipMaps = (ddsimage.getNumMipMaps() > 1); // there is always at least the topmost MipMap - } - - /** - * Load the ImageData for the specified MipMap from original {@link DDSImage}. - * @param mipmap - * @throws UnsupportedDataTypeException - */ - public void loadImageData(int mipmap) throws UnsupportedDataTypeException { - if(mipmap <= this.numMipMaps ) { - - int width = MipMaps.getMipMapSizeAtIndex(mipmap, ddsimage.getWidth()); - int height = MipMaps.getMipMapSizeAtIndex(mipmap, ddsimage.getHeight()); - ByteBuffer data = ddsimage.getMipMap(mipmap).getData(); - - BufferDecompressor bufferDecompressor; - if(isCompressed()) { - CompressionType compressionType = - PixelFormats.getSquishCompressionFormat(ddsimage.getPixelFormat()); - bufferDecompressor = new DXTBufferDecompressor( - data, - width, - height, - compressionType); - } else { - bufferDecompressor = new ARGBBufferDecompressor( - data, - width, - height, - this.pixelformat); - } - this.mipMaps.addMipMap(bufferDecompressor.getImage()); - } - } - - @Override - public void loadImageData() throws UnsupportedDataTypeException { - for (int i = 0; i < this.numMipMaps; i++) { - loadImageData(i); - } - } - - @Override - public String toString() { - return this.file.getAbsolutePath() + PixelFormats.verbosePixelformat(this.pixelformat); - } - - @Override - public boolean equals(Object second) { - if(second != null && second instanceof DDSFile) { - DDSFile secondFile = (DDSFile) second; - return (this.getFile().getAbsoluteFile().equals(secondFile.getFile().getAbsoluteFile()) && - this.hasMipMaps() == secondFile.hasMipMaps() && - this.getPixelformat() == secondFile.getPixelformat() && - this.getHeight() == secondFile.getHeight() && - this.getWidth() == secondFile.getWidth()); - } - return false; - } - -// public ByteBuffer[] getMipMapData() { -// ByteBuffer[] buffer = new ByteBuffer[ddsimage.getNumMipMaps()+1]; -// for (int i = 0; i < buffer.length; i++) { -// buffer[i] = ddsimage.getMipMap(i).getData(); -// } -// return buffer; -// } - - /** - * The DDS-Image can have different texture types. - * Regular Texture, Volume-Texture and CubeMap - * @return TextureType Type of Texture - */ - public TextureType getTextureType() { - return this.textureType; - } - - /** - * The DDS-Image can have different texture types. - * Regular Texture, Volume-Texture and CubeMap - * This returns the textureType from a {@link DDSImage} - * @param ddsimage - * @return - */ - public static TextureType getTextureType(final DDSImage ddsimage) { - if(ddsimage.isCubemap()) { - return TextureType.CUBEMAP; - } else if (ddsimage.isVolume()) { - return TextureType.VOLUME; - } else { - return TextureType.TEXTURE; - } - } - - public void write(final File targetFile) throws IOException { - ByteBuffer[] mipmaps = new ByteBuffer[getNumMipMaps()]; - for (int i = 0; i < mipmaps.length; i++) { - mipmaps[i] = DDSImage.read(this.file).getMipMap(i).getData(); - } - - DDSImage outputDDS = DDSImage.createFromData(this.pixelformat, width, height, mipmaps); - outputDDS.write(file); - outputDDS.close(); - } - - /** - * Checks if the {@link File} is a valid DDS-Image - * @param file - * @return - * @throws IOException - * - */ - public static boolean isValidDDSImage(final File file) throws IOException { - FileInputStream fis = new FileInputStream(file); - boolean isDDSImage = DDSImage.isDDSImage(fis); - fis.close(); - return isDDSImage; - } - - /** - * Returns the stored MipMaps as a {@link BufferedImage}-Array - * @return - */ - public BufferedImage[] getAllMipMapsBI(){ - return mipMaps.getAllMipMapsArray(); - } - - /** - * returns the stored MipMaps as {@link ByteBuffer}-Array - * @return - */ - public List generateAllMipMaps(){ - MipMaps mipMaps = new MipMaps(); - mipMaps.generateMipMaps(getTopMipMap()); - return mipMaps.getAllMipMaps(); - } - - public BufferedImage getMipMap(int index) { - return this.mipMaps.getMipMap(index); - } - -} diff --git a/src/ddsutils/model/MipMaps.java b/src/ddsutils/model/MipMaps.java deleted file mode 100644 index ad1489b..0000000 --- a/src/ddsutils/model/MipMaps.java +++ /dev/null @@ -1,322 +0,0 @@ -/** - * - */ -package model; - -import gr.zdimensions.jsquish.Squish; - -import java.awt.Dimension; -import java.awt.image.BufferedImage; -import java.nio.ByteBuffer; -import java.util.Iterator; -import java.util.List; -import java.util.Vector; - -import jogl.DDSImage; -import ddsutil.ByteBufferedImage; -import ddsutil.ImageRescaler; -import ddsutil.MipMapsUtil; -import ddsutil.NonCubicDimensionException; -import ddsutil.Rescaler; - - -/** - * MipMap Texture contains several layers of MipMaps, each is 1/4 the size of the one above. - * @author Daniel Senff - * - */ -public class MipMaps extends AbstractTextureMap implements Iterable { - - /** - * Topmost MipMap Index - */ - public static final int TOP_MOST_MIP_MAP = 0; - - List mipmaps; - /** - * {@link Rescaler} providing the scaling algorithm. - */ - protected Rescaler rescaler; - - private int numMipMaps; - - /** - * @param topmost - * - */ - public MipMaps() { - this(0); - } - - public MipMaps(final int numMipMaps) { - this.numMipMaps = numMipMaps; - this.rescaler = new ImageRescaler(); - this.mipmaps = new Vector(numMipMaps); - } - - /** - * Populate this MipMap-Object based on the given topmost Map. - * @param topmost - */ - public void generateMipMaps(BufferedImage topmost) { - addMipMap(topmost); - System.out.println("Generate Mipmaps"); - - if(!DDSFile.isPowerOfTwo(topmost.getWidth()) - && !DDSFile.isPowerOfTwo(topmost.getHeight())) - throw new NonCubicDimensionException(); - - generateMipMapArray(); - } - - private void generateMipMapArray() { - BufferedImage topmost = getMipMaps().get(0); - // dimensions of first map - int mipmapWidth = topmost.getWidth(); - int mipmapHeight = topmost.getHeight(); - this.numMipMaps = MipMapsUtil.calculateMaxNumberOfMipMaps(mipmapWidth, mipmapHeight); - - BufferedImage previousMap = topmost; - BufferedImage mipMapBi; - for (int i = 1; i < this.numMipMaps; i++) { - // calculation for next map - mipmapWidth = MipMaps.calculateMipMapSize(mipmapWidth); - mipmapHeight = MipMaps.calculateMipMapSize(mipmapHeight); - - mipMapBi = rescaler.rescaleBI(previousMap, mipmapWidth, mipmapHeight); - addMipMap(mipMapBi); - // by using this map in the next MipMap generation step, we increase - // performance, since we don't always scale from the biggest image. - // however this might also increase errors over generations - previousMap = mipMapBi; - } - } - - /** - * Returns the highest MipMap in the original resolution. - * @return - */ - public BufferedImage getTopMostMipMap() { - return getMipMap(TOP_MOST_MIP_MAP); - } - - /** - * @return - */ - public int getNumMipMaps() { - return this.numMipMaps; - } - - @Override - public int getHeight() { - return getMipMap(TOP_MOST_MIP_MAP).getHeight(); - } - - - @Override - public int getWidth() { - return getMipMap(TOP_MOST_MIP_MAP).getWidth(); - } - - /** - * Returns a Map of the given level. - * @param index - * @return - */ - public BufferedImage getMipMap(final int index) { - return getMipMaps().get(index); - } - - /** - * Set the given {@link BufferedImage} as MipMap in the index. - * @param mipmapIndex - * @param image - */ - public void setMipMap(int mipmapIndex, BufferedImage image) { - if(getMipMaps().size() == mipmapIndex) - addMipMap(mipmapIndex, image); - else - getMipMaps().set(mipmapIndex, image); - } - - private List getMipMaps() { - return this.mipmaps; - } - - /** - * @param image - */ - public void addMipMap(final BufferedImage image) { - getMipMaps().add(image); - } - - private void addMipMap(final int mipmapIndex, final BufferedImage image) { - getMipMaps().add(mipmapIndex, image); - } - - /** - * All contained MipMaps compressed with DXT in {@link ByteBuffer} - * Squishes each mipmap and store in a {@link DDSImage} compatible {@link ByteBuffer}-Array. - * @param compressionType - * @return - */ - @Override - public ByteBuffer[] getDXTCompressedBuffer(final Squish.CompressionType compressionType) { - ByteBuffer[] mipmapBuffer = new ByteBuffer[this.numMipMaps]; - - for (int j = 0; j < this.numMipMaps; j++) { - System.out.println("compress mipmap " + j); - mipmapBuffer[j] = compress(getMipMap(j), compressionType); - } - return mipmapBuffer; - } - - /** - * Returns a Vector with all MipMaps - * @return - */ - public List getAllMipMaps() { - return getMipMaps(); - } - - /** - * Returns an Array of {@link BufferedImage}s of MipMaps. - * @return - */ - public BufferedImage[] getAllMipMapsArray() { - return (BufferedImage[]) getMipMaps().toArray(); - } - - /* (non-Javadoc) - * @see DDSUtil.AbstractTextureMap#getUncompressedBuffer() - */ - public ByteBuffer[] getUncompressedBuffer() { - ByteBuffer[] mipmapBuffer = new ByteBuffer[numMipMaps]; - for (int i = 0; i < numMipMaps; i++) { - mipmapBuffer[i] = ByteBuffer.wrap(ByteBufferedImage.convertBIintoARGBArray(getMipMap(i))); - } - return mipmapBuffer; - } - - /** - * @param topmost - * @param mipmapWidth - * @param mipmapHeight - * @param mipmapBI - * @return - */ - /*public static BufferedImage[] generateMipMaps(final BufferedImage topmost, - int mipmapWidth, - int mipmapHeight, - final BufferedImage[] mipmapBI) { - int i = 0; // cause the first already is set - ImageRescaler rescaler = new ImageRescaler(); - while(true) { - - mipmapBI[i] = rescaler.rescaleBI(mipmapBI[i], mipmapWidth, mipmapHeight); - - if (mipmapWidth == 1 || mipmapHeight == 1) - break; - - i++; - mipmapWidth = calculateMipMapSize(mipmapWidth); - mipmapHeight = calculateMipMapSize(mipmapHeight); - } - return mipmapBI; - }*/ - - /* (non-Javadoc) - * @see java.lang.Iterable#iterator() - */ - @Override - public Iterator iterator() { - return new Iterator() { - int count=0; - - @Override - public boolean hasNext() { - boolean b = count++ < mipmaps.size()-1; - return b; - } - - @Override - public BufferedImage next() { - return mipmaps.get(count); - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - - }; - } - - /** - * Get the {@link Rescaler}. - * @return - */ - public Rescaler getRescaler() { - return this.rescaler; - } - - /** - * Set the {@link Rescaler}. - * @param rescaler - */ - public void setRescaler(Rescaler rescaler) { - this.rescaler = rescaler; - } - - /** - * returns the new size for the next iteration of a generated MipMap - * Usually half the current value, unless current value is 1 - * @param currentValue - * @return - */ - public static int calculateMipMapSize(final int currentValue) { - return (currentValue > 1) ? currentValue/2 : 1; - } - - /** - * Returns the size of the MipMap at the requested index based on the original value - * @param targetIndex - * @param original size at index 0 - * @return - */ - public static int getMipMapSizeAtIndex(final int targetIndex, final int original) { - int newValue = original; - for (int i = 0; i < targetIndex; i++) { - newValue = MipMaps.calculateMipMapSize(newValue); - } - return newValue; - } - - /** - * Width of the MipMap on the specified index. - * @param index - * @return - */ - public int getMipMapWidth(int index) { - return getMipMap(index).getWidth(); - } - - /** - * Width of the MipMap on the specified index. - * @param index - * @return - */ - public int getMipMapHeight(int index) { - return getMipMap(index).getHeight(); - } - - /** - * Returns the {@link Dimension} of the MipMap at the specified index. - * @param index - * @return - */ - public Dimension getMipMapDimension(final int index) { - return new Dimension(getMipMapWidth(index), getMipMapHeight(index)); - } - -} diff --git a/src/ddsutils/model/SingleTextureMap.java b/src/ddsutils/model/SingleTextureMap.java deleted file mode 100644 index 0b330c3..0000000 --- a/src/ddsutils/model/SingleTextureMap.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * - */ -package model; - -import gr.zdimensions.jsquish.Squish.CompressionType; - -import java.awt.image.BufferedImage; -import java.nio.ByteBuffer; - -import ddsutil.ByteBufferedImage; - - -/** - * TextureMap without MipMaps - * @author danielsenff - * - */ -public class SingleTextureMap extends AbstractTextureMap { - - BufferedImage bi; - - /** - * @param bi - */ - public SingleTextureMap(final BufferedImage bi) { - super(); - this.bi = bi; - } - - /** - * @return - */ - public BufferedImage getData() { - return this.bi; - } - - /* (non-Javadoc) - * @see DDSUtil.AbstractMipMaps#getDXTCompressedBuffer(gr.zdimensions.jsquish.Squish.CompressionType) - */ - @Override - public ByteBuffer[] getDXTCompressedBuffer(final CompressionType compressionType) { - ByteBuffer[] buffer = new ByteBuffer[1]; - buffer[0] = super.compress(bi, compressionType); - return buffer; - } - - - - /* (non-Javadoc) - * @see DDSUtil.AbstractMipMaps#getHeight() - */ - @Override - public int getHeight() { - return this.bi.getHeight(); - } - - /* (non-Javadoc) - * @see DDSUtil.AbstractMipMaps#getWidth() - */ - @Override - public int getWidth() { - return this.bi.getWidth(); - } - - /* (non-Javadoc) - * @see DDSUtil.AbstractTextureMap#getUncompressedBuffer() - */ - @Override - public ByteBuffer[] getUncompressedBuffer() { - ByteBuffer[] mipmapBuffer = new ByteBuffer[1]; - mipmapBuffer[0] = ByteBuffer.wrap(ByteBufferedImage.convertBIintoARGBArray(this.bi)); - return mipmapBuffer; - } - -} diff --git a/src/ddsutils/model/TEXFile.java b/src/ddsutils/model/TEXFile.java deleted file mode 100644 index 2ed4fa9..0000000 --- a/src/ddsutils/model/TEXFile.java +++ /dev/null @@ -1,135 +0,0 @@ -package model; - -import gr.zdimensions.jsquish.Squish.CompressionType; - -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.List; - -import javax.activation.UnsupportedDataTypeException; - -import jogl.DDSImage; -import jogl.TEXImage; - -import compression.DXTBufferDecompressor; - -import ddsutil.PixelFormats; - -/** - * TEX for GP4-Texture format. - * @author dahie - * - */ -public class TEXFile extends AbstractTextureImage { - - private TEXImage teximage; - - /** - * @param filename - */ - public TEXFile(final String filename) { - this(new File(filename)); - } - - /** - * @param file - */ - public TEXFile(final File file) { - this.file = file; - TEXImage teximage = null; - try { - teximage = TEXImage.read(file); - init(teximage); - } catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * @param image - */ - protected void init(final TEXImage image) { - this.teximage = image; - this.width = image.getWidth(); - this.height = image.getHeight(); - this.depth = image.getDepth(); - this.pixelformat = image.getPixelFormat(); - this.numMipMaps = image.getNumMipMaps(); - this.mipMaps = new MipMaps(this.numMipMaps); - this.hasMipMaps = (image.getNumMipMaps() > 1); // there is always at least the topmost MipMap - } - - @Override - public void write(final File file) throws IOException { - ByteBuffer[] mipmaps = new ByteBuffer[getNumMipMaps()]; - for (int i = 0; i < mipmaps.length; i++) { - DDSImage image = TEXImage.read(this.file).getAllEmbeddedMaps().get(i); - mipmaps[i] = image.getAllMipMaps()[0].getData(); - } - - TEXImage outputTEX = TEXImage.createFromData(this.pixelformat, width, height, mipmaps); - outputTEX.write(file); - outputTEX.close(); - } - - @Override - public TextureType getTextureType() { - return TextureType.TEXTURE; - } - - /** - * Checks if the {@link File} is a valid DDS-Image - * @param file - * @return - * @throws IOException - * - */ - public static boolean isValidTEXImage(final File file) throws IOException { - FileInputStream fis = new FileInputStream(file); - boolean isTEXImage = TEXImage.isTEXImage(fis); - fis.close(); - return isTEXImage; - } - - /** - * Returns the stored MipMaps as a {@link BufferedImage}-Array - * @return - */ - @Override - public BufferedImage[] getAllMipMapsBI(){ - MipMaps mipMaps = new MipMaps(); - mipMaps.generateMipMaps(getTopMipMap()); - return mipMaps.getAllMipMapsArray(); - } - - /** - * returns the stored MipMaps as {@link ByteBuffer}-Array - * @return - */ - @Override - public List generateAllMipMaps(){ - MipMaps mipMaps = new MipMaps(); - mipMaps.generateMipMaps(getTopMipMap()); - return mipMaps.getAllMipMaps(); - } - - @Override - public void loadImageData() throws UnsupportedDataTypeException { - CompressionType compressionType = - PixelFormats.getSquishCompressionFormat(teximage.getPixelFormat()); - this.mipMaps.setMipMap(0, new DXTBufferDecompressor( - teximage.getEmbeddedMaps(0).getMipMap(0).getData(), - teximage.getWidth(), - teximage.getHeight(), - compressionType).getImage()); - } - - @Override - public BufferedImage getMipMap(final int index) { - return this.mipMaps.getMipMap(index); - } - -} diff --git a/src/ddsutils/model/TextureImage.java b/src/ddsutils/model/TextureImage.java deleted file mode 100644 index abe8fd3..0000000 --- a/src/ddsutils/model/TextureImage.java +++ /dev/null @@ -1,192 +0,0 @@ -package model; - -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.List; - -import javax.activation.UnsupportedDataTypeException; - -public interface TextureImage { - - /** - * TextureType describes what kind of Texture the DDS is. Regular 2D-Texture, Volume or Cubemap. - * - */ - public enum TextureType { - /** - * Regular texture (plus MipMaps) with one slice. - */ - TEXTURE, - /** - * Cubemaps contain 6 slices (including MipMaps) for 6 sides of a cube. - */ - CUBEMAP, - /** - * Volume-textures contain many slices (including MipMaps). - */ - VOLUME - } - - /** - * Topmost MipMap Index - */ - public static final int TOP_MOST_MIP_MAP = 0; - - /** - * Pixelformat describes the way pixels are stored in the DDS. - * Either uncompressed or with a special compression format. - */ - public enum PixelFormat { - DXT5, DXT4, DXT3, DXT2, DXT1, - A8R8G8B8, X8R8G8B8, R8G8B8, - A1R5G5B5, R5G6B5, Unknown - } - - /** - * Width of the topmost MipMap - * @return - */ - public int getHeight(); - - /** - * Height of the topmost MipMap - * @return - */ - public int getWidth(); - - - /** - * Get the Format in which pixel are stored in the file as internal stored Integer-value. - * @return in - */ - public int getPixelformat(); - - /** - * Sets the format in which pixel are stored in the file. - * @param pixelformat - */ - public void setPixelformat(final int pixelformat); - - /** - * Sets the format in which pixel are stored in the file. - * @param pixelformat - */ - public void setPixelformat(final PixelFormat pixelformat); - - /** - * Gets the format in which pixels are stored as a verbose {@link String}. - * @return - */ - public String getPixelformatVerbose(); - - /** - * Returns true if the dds-file is compressed as DXT1-5 - * @return boolean - */ - public boolean isCompressed(); - - /** - * Depth of color for all channels - * @return int - */ - public int getDepth(); - - /** - * Depth of color of each channel - * @return int - */ - public int getChannelDepth(); - - /** - * Returns the absolute path to the {@link File}. - * @return - */ - public String getAbsolutePath(); - - - /** - * Returns the associated {@link File} - * @return File - */ - public File getFile(); - - /** - * Returns whether or not the dds-file has MipMaps. - * Usually only textures whose size is a power of two may have mipmaps. - * @return boolean - */ - public boolean hasMipMaps(); - - /** - * Activates the generation of MipMaps when saving the DDS to disc. - * @param generateMipMaps - * @throws IllegalArgumentException - */ - public void setHasMipMaps(final boolean generateMipMaps) throws IllegalArgumentException; - - /** - * Returns the number of MipMaps in this file. - * @return int Number of MipMaps - */ - public int getNumMipMaps(); - - /** - * The DDS-Image can have different texture types. - * Regular Texture, Volume-Texture and CubeMap - * @return TextureType Type of Texture - */ - public TextureType getTextureType(); - - /** - * Write to disc - * @throws IOException - */ - public void write() throws IOException; - - /** - * Write this Image to disk. - * @param file - * @throws IOException - */ - public void write(final File file) throws IOException; - - - /** - * Returns the topmost MipMap - * @return {@link BufferedImage} - */ - public BufferedImage getData(); - - /** - * Returns the MipMap at the specified index. - * @param index - * @return - */ - public BufferedImage getMipMap(int index); - - /** - * Load the data from file into memory. - * @throws UnsupportedDataTypeException - */ - public void loadImageData() throws UnsupportedDataTypeException; - - /** - * Returns the stored MipMaps as a {@link BufferedImage}-Array - * @return - */ - public BufferedImage[] getAllMipMapsBI(); - - /** - * returns the stored MipMaps as {@link ByteBuffer}-Array - * @return - */ - public List generateAllMipMaps(); - - /** - * Sets a new {@link BufferedImage} as the Topmost MipMap and generates new MipMaps accordingly. - * @param bi - */ - public void setData(final BufferedImage bi); -} diff --git a/src/ddsutils/model/TextureMap.java b/src/ddsutils/model/TextureMap.java deleted file mode 100644 index a4fb9ef..0000000 --- a/src/ddsutils/model/TextureMap.java +++ /dev/null @@ -1,63 +0,0 @@ -package model; - -import gr.zdimensions.jsquish.Squish; - -import java.awt.image.BufferedImage; -import java.nio.ByteBuffer; - -import javax.activation.UnsupportedDataTypeException; - -/** - * Interface for TextureMaps. - * @author danielsenff - * - */ -public interface TextureMap { - - - /** - * Height of the topmost MipMap. - * @return - */ - public int getHeight(); - - /** - * Width of the topmost MipMap. - * @return - */ - public int getWidth(); - - - /** - * All contained MipMaps compressed with DXT in {@link ByteBuffer} - * @param compressionType - * @return - */ - public ByteBuffer[] getDXTCompressedBuffer(final Squish.CompressionType compressionType); - - /** - * All contained MipMaps as {@link ByteBuffer} - * @param compressionType - * @return - */ - public ByteBuffer[] getUncompressedBuffer(); - - /** - * Returns a ByteBuffer for each MipMap. - * @param pixelformat - * @return - * @throws UnsupportedDataTypeException - */ - public ByteBuffer[] getDXTCompressedBuffer(final int pixelformat) - throws UnsupportedDataTypeException; - - /** - * Compress a single {@link BufferedImage} into a ByteBuffer. - * @param bi - * @param compressionType - * @return - */ - public ByteBuffer compress(final BufferedImage bi, - final Squish.CompressionType compressionType); - -} diff --git a/src/ddsutils/util/Debug.java b/src/ddsutils/util/Debug.java deleted file mode 100644 index e91f9cb..0000000 --- a/src/ddsutils/util/Debug.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * - */ -package util; - -/** - * @author danielsenff - * - */ -public class Debug { - - - /** - * Quick debug output for X- and Y-values - * @param rgb - */ - @SuppressWarnings("unused") - private static void sysoXY(final int x, final int y) { - System.out.println("x: "+ x +" y: "+ y); - } - - /** - * Quick debug output for RGBA-Arrays. - * @param rgb - */ - public static void sysoRGBA(int[] rgb) { - System.out.println("R: " + rgb[0] + " G "+ rgb[1] +" B "+ rgb[2] +" A "+ rgb[3]); - } - - /** - * Quick debug output for RGB-Arrays. - * @param rgb - */ - public static void sysoRGBA(byte[] rgb) { - System.out.println("R: " + rgb[0] + " G "+ rgb[1] +" B "+ rgb[2] +" A "+ rgb[3]); - } - -} diff --git a/src/ddsutils/util/FileUtil.java b/src/ddsutils/util/FileUtil.java deleted file mode 100644 index cd60805..0000000 --- a/src/ddsutils/util/FileUtil.java +++ /dev/null @@ -1,129 +0,0 @@ -package util; - -/* - * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - * You acknowledge that this software is not designed or intended for use - * in the design, construction, operation or maintenance of any nuclear - * facility. - * - * Sun gratefully acknowledges that this software was originally authored - * and developed by Kenneth Bradley Russell and Christopher John Kline. - */ - - - -import java.io.File; - -/** Utilities for dealing with files. */ - -public class FileUtil { - private FileUtil() {} - - /** - * Returns the lowercase suffix of the given file name (the text - * after the last '.' in the file name). Returns null if the file - * name has no suffix. Only operates on the given file name; - * performs no I/O operations. - * - * @param file name of the file - * @return lowercase suffix of the file name - * @throws NullPointerException if file is null - */ - - public static String getFileSuffix(File file) { - return getFileSuffix(file.getName()); - } - - /** - * Returns the lowercase suffix of the given file name (the text - * after the last '.' in the file name). Returns null if the file - * name has no suffix. Only operates on the given file name; - * performs no I/O operations. - * - * @param filename name of the file - * @return lowercase suffix of the file name - * @throws NullPointerException if filename is null - */ - public static String getFileSuffix(String filename) { - int lastDot = filename.lastIndexOf('.'); - if (lastDot < 0) { - return ""; - } - return toLowerCase(filename.substring(lastDot + 1)); - } - - private static String toLowerCase(String arg) { - if (arg == null) { - return ""; - } - - return arg.toLowerCase(); - } - - - /** - * Checks whether the extension of the given filename is as the same as the given string. - * This is case-insensitive. - * @param filename - * @param extension - * @return - */ - public static boolean isExtension(final String filename, final String extension) { - return getFileSuffix(filename).toLowerCase().contains(extension.toLowerCase()); - } - - /** - * Checks whether the extension of the given {@link File} is as the same as the given string. - * This is case-insensitive. - * @param file - * @param extension - * @return - */ - public static boolean isExtension(final File file, final String extension) { - return getFileSuffix(file).toLowerCase().contains(extension.toLowerCase()); - } - - /** - * Checks whether the extension of the given {@link File} is as the same as any of the given extensions. - * This is case-insensitive. - * @param file - * @param extensions - * @return - */ - public static boolean isExtension(final File file, final String[] extensions) { - for (int i = 0; i < extensions.length; i++) { - if (getFileSuffix(file).toLowerCase().contains(extensions[i].toLowerCase())) - return true; - } - return false; - } -} diff --git a/src/ddsutils/util/ImageIOUtils.java b/src/ddsutils/util/ImageIOUtils.java deleted file mode 100644 index 6d4e05e..0000000 --- a/src/ddsutils/util/ImageIOUtils.java +++ /dev/null @@ -1,22 +0,0 @@ -package util; - -import java.io.File; - -import javax.imageio.ImageIO; - -public class ImageIOUtils { - - /** - * @param file - * @return - */ - public static boolean isImageIOSupported(final File file) { - String[] supportedMIMETypes = ImageIO.getReaderFormatNames(); - for (int j = 0; j < supportedMIMETypes.length; j++) { - if(FileUtil.getFileSuffix(file).contains(supportedMIMETypes[j])) - return true; - } - return false; - } - -} diff --git a/src/ddsutils/util/ImageUtils.java b/src/ddsutils/util/ImageUtils.java deleted file mode 100644 index b2c0761..0000000 --- a/src/ddsutils/util/ImageUtils.java +++ /dev/null @@ -1,312 +0,0 @@ -package util; - -/* - * Copyright 2009, Morten Nobel-Joergensen - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - - -import java.awt.Graphics2D; -import java.awt.image.BufferedImage; -import java.awt.image.Raster; -import java.awt.image.WritableRaster; - - -/** - * @author Heinz Doerr - */ -public class ImageUtils { - - - - - /** - * @param img - * @return - */ - static public String imageTypeName(final BufferedImage img) { - switch (img.getType()) { - case BufferedImage.TYPE_3BYTE_BGR: return "TYPE_3BYTE_BGR"; - case BufferedImage.TYPE_4BYTE_ABGR: return "TYPE_4BYTE_ABGR"; - case BufferedImage.TYPE_4BYTE_ABGR_PRE: return "TYPE_4BYTE_ABGR_PRE"; - case BufferedImage.TYPE_BYTE_BINARY: return "TYPE_BYTE_BINARY"; - case BufferedImage.TYPE_BYTE_GRAY: return "TYPE_BYTE_GRAY"; - case BufferedImage.TYPE_BYTE_INDEXED: return "TYPE_BYTE_INDEXED"; - case BufferedImage.TYPE_CUSTOM: return "TYPE_CUSTOM"; - case BufferedImage.TYPE_INT_ARGB: return "TYPE_INT_ARGB"; - case BufferedImage.TYPE_INT_ARGB_PRE: return "TYPE_INT_ARGB_PRE"; - case BufferedImage.TYPE_INT_BGR: return "TYPE_INT_BGR"; - case BufferedImage.TYPE_INT_RGB: return "TYPE_INT_RGB"; - case BufferedImage.TYPE_USHORT_555_RGB: return "TYPE_USHORT_555_RGB"; - case BufferedImage.TYPE_USHORT_565_RGB: return "TYPE_USHORT_565_RGB"; - case BufferedImage.TYPE_USHORT_GRAY: return "TYPE_USHORT_GRAY"; - } - return "unknown image type #" + img.getType(); - } - - - /** - * @param img - * @return - */ - static public int nrChannels(final BufferedImage img) { - switch (img.getType()) { - case BufferedImage.TYPE_3BYTE_BGR: return 3; - case BufferedImage.TYPE_4BYTE_ABGR: return 4; - case BufferedImage.TYPE_BYTE_GRAY: return 1; - case BufferedImage.TYPE_INT_BGR: return 3; - case BufferedImage.TYPE_INT_ARGB: return 4; - case BufferedImage.TYPE_INT_RGB: return 3; - case BufferedImage.TYPE_CUSTOM: return 4; - case BufferedImage.TYPE_4BYTE_ABGR_PRE: return 4; - case BufferedImage.TYPE_INT_ARGB_PRE: return 4; - case BufferedImage.TYPE_USHORT_555_RGB: return 3; - case BufferedImage.TYPE_USHORT_565_RGB: return 3; - case BufferedImage.TYPE_USHORT_GRAY: return 1; - } - return 0; - } - - - - - - - /** - * - * returns one row (height == 1) of byte packed image data in BGR or AGBR form - * - * @param img - * @param y - * @param w - * @param array - * @param temp must be either null or a array with length of w*h - * @return - */ - public static byte[] getPixelsBGR(final BufferedImage img, - final int y, final int w, final byte[] array, final int[] temp) { - final int x= 0; - final int h= 1; - - - assert array.length == temp.length * nrChannels(img); - assert (temp.length == w); - - - int imageType= img.getType(); - Raster raster; - switch (imageType) { - case BufferedImage.TYPE_3BYTE_BGR: - case BufferedImage.TYPE_4BYTE_ABGR: - case BufferedImage.TYPE_4BYTE_ABGR_PRE: - case BufferedImage.TYPE_BYTE_GRAY: - raster= img.getRaster(); - //int ttype= raster.getTransferType(); - raster.getDataElements(x, y, w, h, array); - break; - case BufferedImage.TYPE_INT_BGR: - raster= img.getRaster(); - raster.getDataElements(x, y, w, h, temp); - ints2bytes(temp, array, 0, 1, 2); // bgr --> bgr - break; - case BufferedImage.TYPE_INT_RGB: - raster= img.getRaster(); - raster.getDataElements(x, y, w, h, temp); - ints2bytes(temp, array, 2, 1, 0); // rgb --> bgr - break; - case BufferedImage.TYPE_INT_ARGB: - case BufferedImage.TYPE_INT_ARGB_PRE: - raster= img.getRaster(); - raster.getDataElements(x, y, w, h, temp); - ints2bytes(temp, array, 2, 1, 0, 3); // argb --> abgr - break; - case BufferedImage.TYPE_CUSTOM: // TODO: works for my icon image loader, but else ??? - img.getRGB(x, y, w, h, temp, 0, w); - ints2bytes(temp, array, 2, 1, 0, 3); // argb --> abgr - break; - default: - img.getRGB(x, y, w, h, temp, 0, w); - ints2bytes(temp, array, 2, 1, 0); // rgb --> bgr - break; - } - - - return array; - } - - - /** - * converts and copies byte packed BGR or ABGR into the img buffer, - * the img type may vary (e.g. RGB or BGR, int or byte packed) - * but the number of components (w/o alpha, w alpha, gray) must match - * - * does not unmange the image for all (A)RGN and (A)BGR and gray imaged - * @param bgrPixels - * @param img - * @param x - * @param y - * @param w - * @param h - * - */ - public static void setBGRPixels(final byte[] bgrPixels, - final BufferedImage img, final int x, final int y, final int w, final int h) { - int imageType= img.getType(); - WritableRaster raster= img.getRaster(); - //int ttype= raster.getTransferType(); - if (imageType == BufferedImage.TYPE_3BYTE_BGR || - imageType == BufferedImage.TYPE_4BYTE_ABGR || - imageType == BufferedImage.TYPE_4BYTE_ABGR_PRE || - imageType == BufferedImage.TYPE_BYTE_GRAY) { - raster.setDataElements(x, y, w, h, bgrPixels); - } else { - int[] pixels; - if (imageType == BufferedImage.TYPE_INT_BGR) { - pixels= bytes2int(bgrPixels, 2, 1, 0); // bgr --> bgr - } else if (imageType == BufferedImage.TYPE_INT_ARGB || - imageType == BufferedImage.TYPE_INT_ARGB_PRE) { - pixels= bytes2int(bgrPixels, 3, 0, 1, 2); // abgr --> argb - } else { - pixels= bytes2int(bgrPixels, 0, 1, 2); // bgr --> rgb - } - if (w == 0 || h == 0) { - return; - } else if (pixels.length < w * h) { - throw new IllegalArgumentException("pixels array must have a length" + " >= w*h"); - } - if (imageType == BufferedImage.TYPE_INT_ARGB || - imageType == BufferedImage.TYPE_INT_RGB || - imageType == BufferedImage.TYPE_INT_ARGB_PRE || - imageType == BufferedImage.TYPE_INT_BGR) { - raster.setDataElements(x, y, w, h, pixels); - } else { - // Unmanages the image - img.setRGB(x, y, w, h, pixels, 0, w); - } - } - } - - - - - /** - * @param in - * @param out - * @param index1 - * @param index2 - * @param index3 - */ - public static void ints2bytes(final int[] in, - final byte[] out, final int index1, - final int index2, final int index3) { - for (int i= 0; i < in.length; i++) { - int index= i * 3; - int value= in[i]; - out[index + index1]= (byte)value; - value= value >> 8; - out[index + index2]= (byte)value; - value= value >> 8; - out[index + index3]= (byte)value; - } - } - - - /** - * @param in - * @param out - * @param index1 - * @param index2 - * @param index3 - * @param index4 - */ - public static void ints2bytes(final int[] in, final byte[] out, - final int index1, final int index2, - final int index3, final int index4) { - for (int i= 0; i < in.length; i++) { - int index= i * 4; - int value= in[i]; - out[index + index1]= (byte)value; - value= value >> 8; - out[index + index2]= (byte)value; - value= value >> 8; - out[index + index3]= (byte)value; - value= value >> 8; - out[index + index4]= (byte)value; - } - } - - - /** - * @param in - * @param index1 - * @param index2 - * @param index3 - * @return - */ - public static int[] bytes2int(final byte[] in, final int index1, - final int index2, final int index3) { - int[] out= new int[in.length / 3]; - for (int i= 0; i < out.length; i++) { - int index= i * 3; - int b1= (in[index +index1] & 0xff) << 16; - int b2= (in[index + index2] & 0xff) << 8; - int b3= in[index + index3] & 0xff; - out[i]= b1 | b2 | b3; - } - return out; - } - - - /** - * @param in - * @param index1 - * @param index2 - * @param index3 - * @param index4 - * @return - */ - public static int[] bytes2int(final byte[] in, final int index1, - final int index2, final int index3, final int index4) { - int[] out= new int[in.length / 4]; - for (int i= 0; i < out.length; i++) { - int index= i * 4; - int b1= (in[index +index1] & 0xff) << 24; - int b2= (in[index +index2] & 0xff) << 16; - int b3= (in[index + index3] & 0xff) << 8; - int b4= in[index + index4] & 0xff; - out[i]= b1 | b2 | b3 | b4; - } - return out; - } - - - /** - * Converts the {@link BufferedImage} type. - * @param srcImage - * @param destImgType - * @return - */ - public static BufferedImage convert(final BufferedImage srcImage, final int destImgType) { - BufferedImage img= new BufferedImage(srcImage.getWidth(), srcImage.getHeight(), destImgType); - Graphics2D g2d= img.createGraphics(); - g2d.drawImage(srcImage, 0, 0, null); - g2d.dispose(); - return img; - } - - - - -} \ No newline at end of file diff --git a/src/ddsutils/util/Stopwatch.java b/src/ddsutils/util/Stopwatch.java deleted file mode 100644 index bdc26d8..0000000 --- a/src/ddsutils/util/Stopwatch.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * - */ -package util; - -/** - * @author danielsenff - * - */ -public class Stopwatch { - - private long start; - private long stop; - - public void start() { - this.start = System.currentTimeMillis(); - } - - public void stop() { - this.stop = System.currentTimeMillis(); - } - - public void reset() { - this.stop = 0; - this.start = 0; - } - - public long getMilliseconds() { - return stop - start; - } - - public void printMilliseconds() { - System.out.println((stop - start)+ " ms"); - } - - public void printMilliseconds(final String message) { - System.out.println(message + (stop - start) + " ms"); - } -} diff --git a/src/gson/main/java/com/google/gson/DefaultDateTypeAdapter.java b/src/gson/main/java/com/google/gson/DefaultDateTypeAdapter.java deleted file mode 100644 index 95eb42b..0000000 --- a/src/gson/main/java/com/google/gson/DefaultDateTypeAdapter.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -import java.io.IOException; -import java.sql.Timestamp; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.ParsePosition; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; - -import com.google.gson.internal.bind.util.ISO8601Utils; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; - -/** - * This type adapter supports three subclasses of date: Date, Timestamp, and - * java.sql.Date. - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -final class DefaultDateTypeAdapter extends TypeAdapter { - - private static final String SIMPLE_NAME = "DefaultDateTypeAdapter"; - - private final Class dateType; - private final DateFormat enUsFormat; - private final DateFormat localFormat; - - DefaultDateTypeAdapter(Class dateType) { - this(dateType, - DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US), - DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT)); - } - - DefaultDateTypeAdapter(Class dateType, String datePattern) { - this(dateType, new SimpleDateFormat(datePattern, Locale.US), new SimpleDateFormat(datePattern)); - } - - DefaultDateTypeAdapter(Class dateType, int style) { - this(dateType, DateFormat.getDateInstance(style, Locale.US), DateFormat.getDateInstance(style)); - } - - public DefaultDateTypeAdapter(int dateStyle, int timeStyle) { - this(Date.class, - DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US), - DateFormat.getDateTimeInstance(dateStyle, timeStyle)); - } - - public DefaultDateTypeAdapter(Class dateType, int dateStyle, int timeStyle) { - this(dateType, - DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US), - DateFormat.getDateTimeInstance(dateStyle, timeStyle)); - } - - DefaultDateTypeAdapter(final Class dateType, DateFormat enUsFormat, DateFormat localFormat) { - if ( dateType != Date.class && dateType != java.sql.Date.class && dateType != Timestamp.class ) { - throw new IllegalArgumentException("Date type must be one of " + Date.class + ", " + Timestamp.class + ", or " + java.sql.Date.class + " but was " + dateType); - } - this.dateType = dateType; - this.enUsFormat = enUsFormat; - this.localFormat = localFormat; - } - - // These methods need to be synchronized since JDK DateFormat classes are not thread-safe - // See issue 162 - @Override - public void write(JsonWriter out, Date value) throws IOException { - synchronized (localFormat) { - String dateFormatAsString = enUsFormat.format(value); - out.value(dateFormatAsString); - } - } - - @Override - public Date read(JsonReader in) throws IOException { - if (in.peek() != JsonToken.STRING) { - throw new JsonParseException("The date should be a string value"); - } - Date date = deserializeToDate(in.nextString()); - if (dateType == Date.class) { - return date; - } else if (dateType == Timestamp.class) { - return new Timestamp(date.getTime()); - } else if (dateType == java.sql.Date.class) { - return new java.sql.Date(date.getTime()); - } else { - // This must never happen: dateType is guarded in the primary constructor - throw new AssertionError(); - } - } - - private Date deserializeToDate(String s) { - synchronized (localFormat) { - try { - return localFormat.parse(s); - } catch (ParseException ignored) {} - try { - return enUsFormat.parse(s); - } catch (ParseException ignored) {} - try { - return ISO8601Utils.parse(s, new ParsePosition(0)); - } catch (ParseException e) { - throw new JsonSyntaxException(s, e); - } - } - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(SIMPLE_NAME); - sb.append('(').append(localFormat.getClass().getSimpleName()).append(')'); - return sb.toString(); - } -} diff --git a/src/gson/main/java/com/google/gson/ExclusionStrategy.java b/src/gson/main/java/com/google/gson/ExclusionStrategy.java deleted file mode 100644 index 6a3f43f..0000000 --- a/src/gson/main/java/com/google/gson/ExclusionStrategy.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -/** - * A strategy (or policy) definition that is used to decide whether or not a field or top-level - * class should be serialized or deserialized as part of the JSON output/input. For serialization, - * if the {@link #shouldSkipClass(Class)} method returns true then that class or field type - * will not be part of the JSON output. For deserialization, if {@link #shouldSkipClass(Class)} - * returns true, then it will not be set as part of the Java object structure. - * - *

The following are a few examples that shows how you can use this exclusion mechanism. - * - *

Exclude fields and objects based on a particular class type: - *

- * private static class SpecificClassExclusionStrategy implements ExclusionStrategy {
- *   private final Class<?> excludedThisClass;
- *
- *   public SpecificClassExclusionStrategy(Class<?> excludedThisClass) {
- *     this.excludedThisClass = excludedThisClass;
- *   }
- *
- *   public boolean shouldSkipClass(Class<?> clazz) {
- *     return excludedThisClass.equals(clazz);
- *   }
- *
- *   public boolean shouldSkipField(FieldAttributes f) {
- *     return excludedThisClass.equals(f.getDeclaredClass());
- *   }
- * }
- * 
- * - *

Excludes fields and objects based on a particular annotation: - *

- * public @interface FooAnnotation {
- *   // some implementation here
- * }
- *
- * // Excludes any field (or class) that is tagged with an "@FooAnnotation"
- * private static class FooAnnotationExclusionStrategy implements ExclusionStrategy {
- *   public boolean shouldSkipClass(Class<?> clazz) {
- *     return clazz.getAnnotation(FooAnnotation.class) != null;
- *   }
- *
- *   public boolean shouldSkipField(FieldAttributes f) {
- *     return f.getAnnotation(FooAnnotation.class) != null;
- *   }
- * }
- * 
- * - *

Now if you want to configure {@code Gson} to use a user defined exclusion strategy, then - * the {@code GsonBuilder} is required. The following is an example of how you can use the - * {@code GsonBuilder} to configure Gson to use one of the above sample: - *

- * ExclusionStrategy excludeStrings = new UserDefinedExclusionStrategy(String.class);
- * Gson gson = new GsonBuilder()
- *     .setExclusionStrategies(excludeStrings)
- *     .create();
- * 
- * - *

For certain model classes, you may only want to serialize a field, but exclude it for - * deserialization. To do that, you can write an {@code ExclusionStrategy} as per normal; - * however, you would register it with the - * {@link GsonBuilder#addDeserializationExclusionStrategy(ExclusionStrategy)} method. - * For example: - *

- * ExclusionStrategy excludeStrings = new UserDefinedExclusionStrategy(String.class);
- * Gson gson = new GsonBuilder()
- *     .addDeserializationExclusionStrategy(excludeStrings)
- *     .create();
- * 
- * - * @author Inderjeet Singh - * @author Joel Leitch - * - * @see GsonBuilder#setExclusionStrategies(ExclusionStrategy...) - * @see GsonBuilder#addDeserializationExclusionStrategy(ExclusionStrategy) - * @see GsonBuilder#addSerializationExclusionStrategy(ExclusionStrategy) - * - * @since 1.4 - */ -public interface ExclusionStrategy { - - /** - * @param f the field object that is under test - * @return true if the field should be ignored; otherwise false - */ - public boolean shouldSkipField(FieldAttributes f); - - /** - * @param clazz the class object that is under test - * @return true if the class should be ignored; otherwise false - */ - public boolean shouldSkipClass(Class clazz); -} diff --git a/src/gson/main/java/com/google/gson/FieldAttributes.java b/src/gson/main/java/com/google/gson/FieldAttributes.java deleted file mode 100644 index bcabff1..0000000 --- a/src/gson/main/java/com/google/gson/FieldAttributes.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -import com.google.gson.internal.$Gson$Preconditions; -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.lang.reflect.Type; -import java.util.Arrays; -import java.util.Collection; - -/** - * A data object that stores attributes of a field. - * - *

This class is immutable; therefore, it can be safely shared across threads. - * - * @author Inderjeet Singh - * @author Joel Leitch - * - * @since 1.4 - */ -public final class FieldAttributes { - private final Field field; - - /** - * Constructs a Field Attributes object from the {@code f}. - * - * @param f the field to pull attributes from - */ - public FieldAttributes(Field f) { - $Gson$Preconditions.checkNotNull(f); - this.field = f; - } - - /** - * @return the declaring class that contains this field - */ - public Class getDeclaringClass() { - return field.getDeclaringClass(); - } - - /** - * @return the name of the field - */ - public String getName() { - return field.getName(); - } - - /** - *

For example, assume the following class definition: - *

-   * public class Foo {
-   *   private String bar;
-   *   private List<String> red;
-   * }
-   *
-   * Type listParameterizedType = new TypeToken<List<String>>() {}.getType();
-   * 
- * - *

This method would return {@code String.class} for the {@code bar} field and - * {@code listParameterizedType} for the {@code red} field. - * - * @return the specific type declared for this field - */ - public Type getDeclaredType() { - return field.getGenericType(); - } - - /** - * Returns the {@code Class} object that was declared for this field. - * - *

For example, assume the following class definition: - *

-   * public class Foo {
-   *   private String bar;
-   *   private List<String> red;
-   * }
-   * 
- * - *

This method would return {@code String.class} for the {@code bar} field and - * {@code List.class} for the {@code red} field. - * - * @return the specific class object that was declared for the field - */ - public Class getDeclaredClass() { - return field.getType(); - } - - /** - * Return the {@code T} annotation object from this field if it exist; otherwise returns - * {@code null}. - * - * @param annotation the class of the annotation that will be retrieved - * @return the annotation instance if it is bound to the field; otherwise {@code null} - */ - public T getAnnotation(Class annotation) { - return field.getAnnotation(annotation); - } - - /** - * Return the annotations that are present on this field. - * - * @return an array of all the annotations set on the field - * @since 1.4 - */ - public Collection getAnnotations() { - return Arrays.asList(field.getAnnotations()); - } - - /** - * Returns {@code true} if the field is defined with the {@code modifier}. - * - *

This method is meant to be called as: - *

-   * boolean hasPublicModifier = fieldAttribute.hasModifier(java.lang.reflect.Modifier.PUBLIC);
-   * 
- * - * @see java.lang.reflect.Modifier - */ - public boolean hasModifier(int modifier) { - return (field.getModifiers() & modifier) != 0; - } - - /** - * This is exposed internally only for the removing synthetic fields from the JSON output. - * - * @return true if the field is synthetic; otherwise false - * @throws IllegalAccessException - * @throws IllegalArgumentException - */ - Object get(Object instance) throws IllegalAccessException { - return field.get(instance); - } - - /** - * This is exposed internally only for the removing synthetic fields from the JSON output. - * - * @return true if the field is synthetic; otherwise false - */ - boolean isSynthetic() { - return field.isSynthetic(); - } -} diff --git a/src/gson/main/java/com/google/gson/FieldNamingPolicy.java b/src/gson/main/java/com/google/gson/FieldNamingPolicy.java deleted file mode 100644 index ad7bb6c..0000000 --- a/src/gson/main/java/com/google/gson/FieldNamingPolicy.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -import java.lang.reflect.Field; -import java.util.Locale; - -/** - * An enumeration that defines a few standard naming conventions for JSON field names. - * This enumeration should be used in conjunction with {@link com.google.gson.GsonBuilder} - * to configure a {@link com.google.gson.Gson} instance to properly translate Java field - * names into the desired JSON field names. - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -public enum FieldNamingPolicy implements FieldNamingStrategy { - - /** - * Using this naming policy with Gson will ensure that the field name is - * unchanged. - */ - IDENTITY() { - @Override public String translateName(Field f) { - return f.getName(); - } - }, - - /** - * Using this naming policy with Gson will ensure that the first "letter" of the Java - * field name is capitalized when serialized to its JSON form. - * - *

Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":

- *
    - *
  • someFieldName ---> SomeFieldName
  • - *
  • _someFieldName ---> _SomeFieldName
  • - *
- */ - UPPER_CAMEL_CASE() { - @Override public String translateName(Field f) { - return upperCaseFirstLetter(f.getName()); - } - }, - - /** - * Using this naming policy with Gson will ensure that the first "letter" of the Java - * field name is capitalized when serialized to its JSON form and the words will be - * separated by a space. - * - *

Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":

- *
    - *
  • someFieldName ---> Some Field Name
  • - *
  • _someFieldName ---> _Some Field Name
  • - *
- * - * @since 1.4 - */ - UPPER_CAMEL_CASE_WITH_SPACES() { - @Override public String translateName(Field f) { - return upperCaseFirstLetter(separateCamelCase(f.getName(), " ")); - } - }, - - /** - * Using this naming policy with Gson will modify the Java Field name from its camel cased - * form to a lower case field name where each word is separated by an underscore (_). - * - *

Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":

- *
    - *
  • someFieldName ---> some_field_name
  • - *
  • _someFieldName ---> _some_field_name
  • - *
  • aStringField ---> a_string_field
  • - *
  • aURL ---> a_u_r_l
  • - *
- */ - LOWER_CASE_WITH_UNDERSCORES() { - @Override public String translateName(Field f) { - return separateCamelCase(f.getName(), "_").toLowerCase(Locale.ENGLISH); - } - }, - - /** - * Using this naming policy with Gson will modify the Java Field name from its camel cased - * form to a lower case field name where each word is separated by a dash (-). - * - *

Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":

- *
    - *
  • someFieldName ---> some-field-name
  • - *
  • _someFieldName ---> _some-field-name
  • - *
  • aStringField ---> a-string-field
  • - *
  • aURL ---> a-u-r-l
  • - *
- * Using dashes in JavaScript is not recommended since dash is also used for a minus sign in - * expressions. This requires that a field named with dashes is always accessed as a quoted - * property like {@code myobject['my-field']}. Accessing it as an object field - * {@code myobject.my-field} will result in an unintended javascript expression. - * @since 1.4 - */ - LOWER_CASE_WITH_DASHES() { - @Override public String translateName(Field f) { - return separateCamelCase(f.getName(), "-").toLowerCase(Locale.ENGLISH); - } - }; - - /** - * Converts the field name that uses camel-case define word separation into - * separate words that are separated by the provided {@code separatorString}. - */ - static String separateCamelCase(String name, String separator) { - StringBuilder translation = new StringBuilder(); - for (int i = 0, length = name.length(); i < length; i++) { - char character = name.charAt(i); - if (Character.isUpperCase(character) && translation.length() != 0) { - translation.append(separator); - } - translation.append(character); - } - return translation.toString(); - } - - /** - * Ensures the JSON field names begins with an upper case letter. - */ - static String upperCaseFirstLetter(String name) { - StringBuilder fieldNameBuilder = new StringBuilder(); - int index = 0; - char firstCharacter = name.charAt(index); - int length = name.length(); - - while (index < length - 1) { - if (Character.isLetter(firstCharacter)) { - break; - } - - fieldNameBuilder.append(firstCharacter); - firstCharacter = name.charAt(++index); - } - - if (!Character.isUpperCase(firstCharacter)) { - String modifiedTarget = modifyString(Character.toUpperCase(firstCharacter), name, ++index); - return fieldNameBuilder.append(modifiedTarget).toString(); - } else { - return name; - } - } - - private static String modifyString(char firstCharacter, String srcString, int indexOfSubstring) { - return (indexOfSubstring < srcString.length()) - ? firstCharacter + srcString.substring(indexOfSubstring) - : String.valueOf(firstCharacter); - } -} diff --git a/src/gson/main/java/com/google/gson/FieldNamingStrategy.java b/src/gson/main/java/com/google/gson/FieldNamingStrategy.java deleted file mode 100644 index 9be453a..0000000 --- a/src/gson/main/java/com/google/gson/FieldNamingStrategy.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -import java.lang.reflect.Field; - -/** - * A mechanism for providing custom field naming in Gson. This allows the client code to translate - * field names into a particular convention that is not supported as a normal Java field - * declaration rules. For example, Java does not support "-" characters in a field name. - * - * @author Inderjeet Singh - * @author Joel Leitch - * @since 1.3 - */ -public interface FieldNamingStrategy { - - /** - * Translates the field name into its JSON field name representation. - * - * @param f the field object that we are translating - * @return the translated field name. - * @since 1.3 - */ - public String translateName(Field f); -} diff --git a/src/gson/main/java/com/google/gson/Gson.java b/src/gson/main/java/com/google/gson/Gson.java deleted file mode 100644 index 2828573..0000000 --- a/src/gson/main/java/com/google/gson/Gson.java +++ /dev/null @@ -1,989 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -import java.io.EOFException; -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.io.StringWriter; -import java.io.Writer; -import java.lang.reflect.Type; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicLongArray; - -import com.google.gson.internal.ConstructorConstructor; -import com.google.gson.internal.Excluder; -import com.google.gson.internal.Primitives; -import com.google.gson.internal.Streams; -import com.google.gson.internal.bind.ArrayTypeAdapter; -import com.google.gson.internal.bind.CollectionTypeAdapterFactory; -import com.google.gson.internal.bind.DateTypeAdapter; -import com.google.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory; -import com.google.gson.internal.bind.JsonTreeReader; -import com.google.gson.internal.bind.JsonTreeWriter; -import com.google.gson.internal.bind.MapTypeAdapterFactory; -import com.google.gson.internal.bind.ObjectTypeAdapter; -import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory; -import com.google.gson.internal.bind.SqlDateTypeAdapter; -import com.google.gson.internal.bind.TimeTypeAdapter; -import com.google.gson.internal.bind.TypeAdapters; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import com.google.gson.stream.MalformedJsonException; - -/** - * This is the main class for using Gson. Gson is typically used by first constructing a - * Gson instance and then invoking {@link #toJson(Object)} or {@link #fromJson(String, Class)} - * methods on it. Gson instances are Thread-safe so you can reuse them freely across multiple - * threads. - * - *

You can create a Gson instance by invoking {@code new Gson()} if the default configuration - * is all you need. You can also use {@link GsonBuilder} to build a Gson instance with various - * configuration options such as versioning support, pretty printing, custom - * {@link JsonSerializer}s, {@link JsonDeserializer}s, and {@link InstanceCreator}s.

- * - *

Here is an example of how Gson is used for a simple Class: - * - *

- * Gson gson = new Gson(); // Or use new GsonBuilder().create();
- * MyType target = new MyType();
- * String json = gson.toJson(target); // serializes target to Json
- * MyType target2 = gson.fromJson(json, MyType.class); // deserializes json into target2
- * 

- * - *

If the object that your are serializing/deserializing is a {@code ParameterizedType} - * (i.e. contains at least one type parameter and may be an array) then you must use the - * {@link #toJson(Object, Type)} or {@link #fromJson(String, Type)} method. Here is an - * example for serializing and deserializing a {@code ParameterizedType}: - * - *

- * Type listType = new TypeToken<List<String>>() {}.getType();
- * List<String> target = new LinkedList<String>();
- * target.add("blah");
- *
- * Gson gson = new Gson();
- * String json = gson.toJson(target, listType);
- * List<String> target2 = gson.fromJson(json, listType);
- * 

- * - *

See the Gson User Guide - * for a more complete set of examples.

- * - * @see com.google.gson.reflect.TypeToken - * - * @author Inderjeet Singh - * @author Joel Leitch - * @author Jesse Wilson - */ -public final class Gson { - static final boolean DEFAULT_JSON_NON_EXECUTABLE = false; - static final boolean DEFAULT_LENIENT = false; - static final boolean DEFAULT_PRETTY_PRINT = false; - static final boolean DEFAULT_ESCAPE_HTML = true; - static final boolean DEFAULT_SERIALIZE_NULLS = false; - static final boolean DEFAULT_COMPLEX_MAP_KEYS = false; - static final boolean DEFAULT_SPECIALIZE_FLOAT_VALUES = false; - - private static final TypeToken NULL_KEY_SURROGATE = TypeToken.get(Object.class); - private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n"; - - /** - * This thread local guards against reentrant calls to getAdapter(). In - * certain object graphs, creating an adapter for a type may recursively - * require an adapter for the same type! Without intervention, the recursive - * lookup would stack overflow. We cheat by returning a proxy type adapter. - * The proxy is wired up once the initial adapter has been created. - */ - private final ThreadLocal, FutureTypeAdapter>> calls - = new ThreadLocal, FutureTypeAdapter>>(); - - private final Map, TypeAdapter> typeTokenCache = new ConcurrentHashMap, TypeAdapter>(); - - private final List factories; - private final ConstructorConstructor constructorConstructor; - - private final Excluder excluder; - private final FieldNamingStrategy fieldNamingStrategy; - private final boolean serializeNulls; - private final boolean htmlSafe; - private final boolean generateNonExecutableJson; - private final boolean prettyPrinting; - private final boolean lenient; - private final JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory; - - /** - * Constructs a Gson object with default configuration. The default configuration has the - * following settings: - *
    - *
  • The JSON generated by toJson methods is in compact representation. This - * means that all the unneeded white-space is removed. You can change this behavior with - * {@link GsonBuilder#setPrettyPrinting()}.
  • - *
  • The generated JSON omits all the fields that are null. Note that nulls in arrays are - * kept as is since an array is an ordered list. Moreover, if a field is not null, but its - * generated JSON is empty, the field is kept. You can configure Gson to serialize null values - * by setting {@link GsonBuilder#serializeNulls()}.
  • - *
  • Gson provides default serialization and deserialization for Enums, {@link Map}, - * {@link java.net.URL}, {@link java.net.URI}, {@link java.util.Locale}, {@link java.util.Date}, - * {@link java.math.BigDecimal}, and {@link java.math.BigInteger} classes. If you would prefer - * to change the default representation, you can do so by registering a type adapter through - * {@link GsonBuilder#registerTypeAdapter(Type, Object)}.
  • - *
  • The default Date format is same as {@link java.text.DateFormat#DEFAULT}. This format - * ignores the millisecond portion of the date during serialization. You can change - * this by invoking {@link GsonBuilder#setDateFormat(int)} or - * {@link GsonBuilder#setDateFormat(String)}.
  • - *
  • By default, Gson ignores the {@link com.google.gson.annotations.Expose} annotation. - * You can enable Gson to serialize/deserialize only those fields marked with this annotation - * through {@link GsonBuilder#excludeFieldsWithoutExposeAnnotation()}.
  • - *
  • By default, Gson ignores the {@link com.google.gson.annotations.Since} annotation. You - * can enable Gson to use this annotation through {@link GsonBuilder#setVersion(double)}.
  • - *
  • The default field naming policy for the output Json is same as in Java. So, a Java class - * field versionNumber will be output as "versionNumber" in - * Json. The same rules are applied for mapping incoming Json to the Java classes. You can - * change this policy through {@link GsonBuilder#setFieldNamingPolicy(FieldNamingPolicy)}.
  • - *
  • By default, Gson excludes transient or static fields from - * consideration for serialization and deserialization. You can change this behavior through - * {@link GsonBuilder#excludeFieldsWithModifiers(int...)}.
  • - *
- */ - public Gson() { - this(Excluder.DEFAULT, FieldNamingPolicy.IDENTITY, - Collections.>emptyMap(), DEFAULT_SERIALIZE_NULLS, - DEFAULT_COMPLEX_MAP_KEYS, DEFAULT_JSON_NON_EXECUTABLE, DEFAULT_ESCAPE_HTML, - DEFAULT_PRETTY_PRINT, DEFAULT_LENIENT, DEFAULT_SPECIALIZE_FLOAT_VALUES, - LongSerializationPolicy.DEFAULT, Collections.emptyList()); - } - - Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingStrategy, - final Map> instanceCreators, boolean serializeNulls, - boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe, - boolean prettyPrinting, boolean lenient, boolean serializeSpecialFloatingPointValues, - LongSerializationPolicy longSerializationPolicy, - List typeAdapterFactories) { - this.constructorConstructor = new ConstructorConstructor(instanceCreators); - this.excluder = excluder; - this.fieldNamingStrategy = fieldNamingStrategy; - this.serializeNulls = serializeNulls; - this.generateNonExecutableJson = generateNonExecutableGson; - this.htmlSafe = htmlSafe; - this.prettyPrinting = prettyPrinting; - this.lenient = lenient; - - List factories = new ArrayList(); - - // built-in type adapters that cannot be overridden - factories.add(TypeAdapters.JSON_ELEMENT_FACTORY); - factories.add(ObjectTypeAdapter.FACTORY); - - // the excluder must precede all adapters that handle user-defined types - factories.add(excluder); - - // user's type adapters - factories.addAll(typeAdapterFactories); - - // type adapters for basic platform types - factories.add(TypeAdapters.STRING_FACTORY); - factories.add(TypeAdapters.INTEGER_FACTORY); - factories.add(TypeAdapters.BOOLEAN_FACTORY); - factories.add(TypeAdapters.BYTE_FACTORY); - factories.add(TypeAdapters.SHORT_FACTORY); - TypeAdapter longAdapter = longAdapter(longSerializationPolicy); - factories.add(TypeAdapters.newFactory(long.class, Long.class, longAdapter)); - factories.add(TypeAdapters.newFactory(double.class, Double.class, - doubleAdapter(serializeSpecialFloatingPointValues))); - factories.add(TypeAdapters.newFactory(float.class, Float.class, - floatAdapter(serializeSpecialFloatingPointValues))); - factories.add(TypeAdapters.NUMBER_FACTORY); - factories.add(TypeAdapters.ATOMIC_INTEGER_FACTORY); - factories.add(TypeAdapters.ATOMIC_BOOLEAN_FACTORY); - factories.add(TypeAdapters.newFactory(AtomicLong.class, atomicLongAdapter(longAdapter))); - factories.add(TypeAdapters.newFactory(AtomicLongArray.class, atomicLongArrayAdapter(longAdapter))); - factories.add(TypeAdapters.ATOMIC_INTEGER_ARRAY_FACTORY); - factories.add(TypeAdapters.CHARACTER_FACTORY); - factories.add(TypeAdapters.STRING_BUILDER_FACTORY); - factories.add(TypeAdapters.STRING_BUFFER_FACTORY); - factories.add(TypeAdapters.newFactory(BigDecimal.class, TypeAdapters.BIG_DECIMAL)); - factories.add(TypeAdapters.newFactory(BigInteger.class, TypeAdapters.BIG_INTEGER)); - factories.add(TypeAdapters.URL_FACTORY); - factories.add(TypeAdapters.URI_FACTORY); - factories.add(TypeAdapters.UUID_FACTORY); - factories.add(TypeAdapters.CURRENCY_FACTORY); - factories.add(TypeAdapters.LOCALE_FACTORY); - factories.add(TypeAdapters.INET_ADDRESS_FACTORY); - factories.add(TypeAdapters.BIT_SET_FACTORY); - factories.add(DateTypeAdapter.FACTORY); - factories.add(TypeAdapters.CALENDAR_FACTORY); - factories.add(TimeTypeAdapter.FACTORY); - factories.add(SqlDateTypeAdapter.FACTORY); - factories.add(TypeAdapters.TIMESTAMP_FACTORY); - factories.add(ArrayTypeAdapter.FACTORY); - factories.add(TypeAdapters.CLASS_FACTORY); - - // type adapters for composite and user-defined types - factories.add(new CollectionTypeAdapterFactory(constructorConstructor)); - factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization)); - this.jsonAdapterFactory = new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor); - factories.add(jsonAdapterFactory); - factories.add(TypeAdapters.ENUM_FACTORY); - factories.add(new ReflectiveTypeAdapterFactory( - constructorConstructor, fieldNamingStrategy, excluder, jsonAdapterFactory)); - - this.factories = Collections.unmodifiableList(factories); - } - - public Excluder excluder() { - return excluder; - } - - public FieldNamingStrategy fieldNamingStrategy() { - return fieldNamingStrategy; - } - - public boolean serializeNulls() { - return serializeNulls; - } - - public boolean htmlSafe() { - return htmlSafe; - } - - private TypeAdapter doubleAdapter(boolean serializeSpecialFloatingPointValues) { - if (serializeSpecialFloatingPointValues) { - return TypeAdapters.DOUBLE; - } - return new TypeAdapter() { - @Override public Double read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - return in.nextDouble(); - } - @Override public void write(JsonWriter out, Number value) throws IOException { - if (value == null) { - out.nullValue(); - return; - } - double doubleValue = value.doubleValue(); - checkValidFloatingPoint(doubleValue); - out.value(value); - } - }; - } - - private TypeAdapter floatAdapter(boolean serializeSpecialFloatingPointValues) { - if (serializeSpecialFloatingPointValues) { - return TypeAdapters.FLOAT; - } - return new TypeAdapter() { - @Override public Float read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - return (float) in.nextDouble(); - } - @Override public void write(JsonWriter out, Number value) throws IOException { - if (value == null) { - out.nullValue(); - return; - } - float floatValue = value.floatValue(); - checkValidFloatingPoint(floatValue); - out.value(value); - } - }; - } - - static void checkValidFloatingPoint(double value) { - if (Double.isNaN(value) || Double.isInfinite(value)) { - throw new IllegalArgumentException(value - + " is not a valid double value as per JSON specification. To override this" - + " behavior, use GsonBuilder.serializeSpecialFloatingPointValues() method."); - } - } - - private static TypeAdapter longAdapter(LongSerializationPolicy longSerializationPolicy) { - if (longSerializationPolicy == LongSerializationPolicy.DEFAULT) { - return TypeAdapters.LONG; - } - return new TypeAdapter() { - @Override public Number read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - return in.nextLong(); - } - @Override public void write(JsonWriter out, Number value) throws IOException { - if (value == null) { - out.nullValue(); - return; - } - out.value(value.toString()); - } - }; - } - - private static TypeAdapter atomicLongAdapter(final TypeAdapter longAdapter) { - return new TypeAdapter() { - @Override public void write(JsonWriter out, AtomicLong value) throws IOException { - longAdapter.write(out, value.get()); - } - @Override public AtomicLong read(JsonReader in) throws IOException { - Number value = longAdapter.read(in); - return new AtomicLong(value.longValue()); - } - }.nullSafe(); - } - - private static TypeAdapter atomicLongArrayAdapter(final TypeAdapter longAdapter) { - return new TypeAdapter() { - @Override public void write(JsonWriter out, AtomicLongArray value) throws IOException { - out.beginArray(); - for (int i = 0, length = value.length(); i < length; i++) { - longAdapter.write(out, value.get(i)); - } - out.endArray(); - } - @Override public AtomicLongArray read(JsonReader in) throws IOException { - List list = new ArrayList(); - in.beginArray(); - while (in.hasNext()) { - long value = longAdapter.read(in).longValue(); - list.add(value); - } - in.endArray(); - int length = list.size(); - AtomicLongArray array = new AtomicLongArray(length); - for (int i = 0; i < length; ++i) { - array.set(i, list.get(i)); - } - return array; - } - }.nullSafe(); - } - - /** - * Returns the type adapter for {@code} type. - * - * @throws IllegalArgumentException if this GSON cannot serialize and - * deserialize {@code type}. - */ - @SuppressWarnings("unchecked") - public TypeAdapter getAdapter(TypeToken type) { - TypeAdapter cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type); - if (cached != null) { - return (TypeAdapter) cached; - } - - Map, FutureTypeAdapter> threadCalls = calls.get(); - boolean requiresThreadLocalCleanup = false; - if (threadCalls == null) { - threadCalls = new HashMap, FutureTypeAdapter>(); - calls.set(threadCalls); - requiresThreadLocalCleanup = true; - } - - // the key and value type parameters always agree - FutureTypeAdapter ongoingCall = (FutureTypeAdapter) threadCalls.get(type); - if (ongoingCall != null) { - return ongoingCall; - } - - try { - FutureTypeAdapter call = new FutureTypeAdapter(); - threadCalls.put(type, call); - - for (TypeAdapterFactory factory : factories) { - TypeAdapter candidate = factory.create(this, type); - if (candidate != null) { - call.setDelegate(candidate); - typeTokenCache.put(type, candidate); - return candidate; - } - } - throw new IllegalArgumentException("GSON cannot handle " + type); - } finally { - threadCalls.remove(type); - - if (requiresThreadLocalCleanup) { - calls.remove(); - } - } - } - - /** - * This method is used to get an alternate type adapter for the specified type. This is used - * to access a type adapter that is overridden by a {@link TypeAdapterFactory} that you - * may have registered. This features is typically used when you want to register a type - * adapter that does a little bit of work but then delegates further processing to the Gson - * default type adapter. Here is an example: - *

Let's say we want to write a type adapter that counts the number of objects being read - * from or written to JSON. We can achieve this by writing a type adapter factory that uses - * the getDelegateAdapter method: - *

 {@code
-   *  class StatsTypeAdapterFactory implements TypeAdapterFactory {
-   *    public int numReads = 0;
-   *    public int numWrites = 0;
-   *    public  TypeAdapter create(Gson gson, TypeToken type) {
-   *      final TypeAdapter delegate = gson.getDelegateAdapter(this, type);
-   *      return new TypeAdapter() {
-   *        public void write(JsonWriter out, T value) throws IOException {
-   *          ++numWrites;
-   *          delegate.write(out, value);
-   *        }
-   *        public T read(JsonReader in) throws IOException {
-   *          ++numReads;
-   *          return delegate.read(in);
-   *        }
-   *      };
-   *    }
-   *  }
-   *  } 
- * This factory can now be used like this: - *
 {@code
-   *  StatsTypeAdapterFactory stats = new StatsTypeAdapterFactory();
-   *  Gson gson = new GsonBuilder().registerTypeAdapterFactory(stats).create();
-   *  // Call gson.toJson() and fromJson methods on objects
-   *  System.out.println("Num JSON reads" + stats.numReads);
-   *  System.out.println("Num JSON writes" + stats.numWrites);
-   *  }
- * Note that this call will skip all factories registered before {@code skipPast}. In case of - * multiple TypeAdapterFactories registered it is up to the caller of this function to insure - * that the order of registration does not prevent this method from reaching a factory they - * would expect to reply from this call. - * Note that since you can not override type adapter factories for String and Java primitive - * types, our stats factory will not count the number of String or primitives that will be - * read or written. - * @param skipPast The type adapter factory that needs to be skipped while searching for - * a matching type adapter. In most cases, you should just pass this (the type adapter - * factory from where {@link #getDelegateAdapter} method is being invoked). - * @param type Type for which the delegate adapter is being searched for. - * - * @since 2.2 - */ - public TypeAdapter getDelegateAdapter(TypeAdapterFactory skipPast, TypeToken type) { - // Hack. If the skipPast factory isn't registered, assume the factory is being requested via - // our @JsonAdapter annotation. - if (!factories.contains(skipPast)) { - skipPast = jsonAdapterFactory; - } - - boolean skipPastFound = false; - for (TypeAdapterFactory factory : factories) { - if (!skipPastFound) { - if (factory == skipPast) { - skipPastFound = true; - } - continue; - } - - TypeAdapter candidate = factory.create(this, type); - if (candidate != null) { - return candidate; - } - } - throw new IllegalArgumentException("GSON cannot serialize " + type); - } - - /** - * Returns the type adapter for {@code} type. - * - * @throws IllegalArgumentException if this GSON cannot serialize and - * deserialize {@code type}. - */ - public TypeAdapter getAdapter(Class type) { - return getAdapter(TypeToken.get(type)); - } - - /** - * This method serializes the specified object into its equivalent representation as a tree of - * {@link JsonElement}s. This method should be used when the specified object is not a generic - * type. This method uses {@link Class#getClass()} to get the type for the specified object, but - * the {@code getClass()} loses the generic type information because of the Type Erasure feature - * of Java. Note that this method works fine if the any of the object fields are of generic type, - * just the object itself should not be of a generic type. If the object is of generic type, use - * {@link #toJsonTree(Object, Type)} instead. - * - * @param src the object for which Json representation is to be created setting for Gson - * @return Json representation of {@code src}. - * @since 1.4 - */ - public JsonElement toJsonTree(Object src) { - if (src == null) { - return JsonNull.INSTANCE; - } - return toJsonTree(src, src.getClass()); - } - - /** - * This method serializes the specified object, including those of generic types, into its - * equivalent representation as a tree of {@link JsonElement}s. This method must be used if the - * specified object is a generic type. For non-generic objects, use {@link #toJsonTree(Object)} - * instead. - * - * @param src the object for which JSON representation is to be created - * @param typeOfSrc The specific genericized type of src. You can obtain - * this type by using the {@link com.google.gson.reflect.TypeToken} class. For example, - * to get the type for {@code Collection}, you should use: - *
-   * Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
-   * 
- * @return Json representation of {@code src} - * @since 1.4 - */ - public JsonElement toJsonTree(Object src, Type typeOfSrc) { - JsonTreeWriter writer = new JsonTreeWriter(); - toJson(src, typeOfSrc, writer); - return writer.get(); - } - - /** - * This method serializes the specified object into its equivalent Json representation. - * This method should be used when the specified object is not a generic type. This method uses - * {@link Class#getClass()} to get the type for the specified object, but the - * {@code getClass()} loses the generic type information because of the Type Erasure feature - * of Java. Note that this method works fine if the any of the object fields are of generic type, - * just the object itself should not be of a generic type. If the object is of generic type, use - * {@link #toJson(Object, Type)} instead. If you want to write out the object to a - * {@link Writer}, use {@link #toJson(Object, Appendable)} instead. - * - * @param src the object for which Json representation is to be created setting for Gson - * @return Json representation of {@code src}. - */ - public String toJson(Object src) { - if (src == null) { - return toJson(JsonNull.INSTANCE); - } - return toJson(src, src.getClass()); - } - - /** - * This method serializes the specified object, including those of generic types, into its - * equivalent Json representation. This method must be used if the specified object is a generic - * type. For non-generic objects, use {@link #toJson(Object)} instead. If you want to write out - * the object to a {@link Appendable}, use {@link #toJson(Object, Type, Appendable)} instead. - * - * @param src the object for which JSON representation is to be created - * @param typeOfSrc The specific genericized type of src. You can obtain - * this type by using the {@link com.google.gson.reflect.TypeToken} class. For example, - * to get the type for {@code Collection}, you should use: - *
-   * Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
-   * 
- * @return Json representation of {@code src} - */ - public String toJson(Object src, Type typeOfSrc) { - StringWriter writer = new StringWriter(); - toJson(src, typeOfSrc, writer); - return writer.toString(); - } - - /** - * This method serializes the specified object into its equivalent Json representation. - * This method should be used when the specified object is not a generic type. This method uses - * {@link Class#getClass()} to get the type for the specified object, but the - * {@code getClass()} loses the generic type information because of the Type Erasure feature - * of Java. Note that this method works fine if the any of the object fields are of generic type, - * just the object itself should not be of a generic type. If the object is of generic type, use - * {@link #toJson(Object, Type, Appendable)} instead. - * - * @param src the object for which Json representation is to be created setting for Gson - * @param writer Writer to which the Json representation needs to be written - * @throws JsonIOException if there was a problem writing to the writer - * @since 1.2 - */ - public void toJson(Object src, Appendable writer) throws JsonIOException { - if (src != null) { - toJson(src, src.getClass(), writer); - } else { - toJson(JsonNull.INSTANCE, writer); - } - } - - /** - * This method serializes the specified object, including those of generic types, into its - * equivalent Json representation. This method must be used if the specified object is a generic - * type. For non-generic objects, use {@link #toJson(Object, Appendable)} instead. - * - * @param src the object for which JSON representation is to be created - * @param typeOfSrc The specific genericized type of src. You can obtain - * this type by using the {@link com.google.gson.reflect.TypeToken} class. For example, - * to get the type for {@code Collection}, you should use: - *
-   * Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
-   * 
- * @param writer Writer to which the Json representation of src needs to be written. - * @throws JsonIOException if there was a problem writing to the writer - * @since 1.2 - */ - public void toJson(Object src, Type typeOfSrc, Appendable writer) throws JsonIOException { - try { - JsonWriter jsonWriter = newJsonWriter(Streams.writerForAppendable(writer)); - toJson(src, typeOfSrc, jsonWriter); - } catch (IOException e) { - throw new JsonIOException(e); - } - } - - /** - * Writes the JSON representation of {@code src} of type {@code typeOfSrc} to - * {@code writer}. - * @throws JsonIOException if there was a problem writing to the writer - */ - @SuppressWarnings("unchecked") - public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException { - TypeAdapter adapter = getAdapter(TypeToken.get(typeOfSrc)); - boolean oldLenient = writer.isLenient(); - writer.setLenient(true); - boolean oldHtmlSafe = writer.isHtmlSafe(); - writer.setHtmlSafe(htmlSafe); - boolean oldSerializeNulls = writer.getSerializeNulls(); - writer.setSerializeNulls(serializeNulls); - try { - ((TypeAdapter) adapter).write(writer, src); - } catch (IOException e) { - throw new JsonIOException(e); - } finally { - writer.setLenient(oldLenient); - writer.setHtmlSafe(oldHtmlSafe); - writer.setSerializeNulls(oldSerializeNulls); - } - } - - /** - * Converts a tree of {@link JsonElement}s into its equivalent JSON representation. - * - * @param jsonElement root of a tree of {@link JsonElement}s - * @return JSON String representation of the tree - * @since 1.4 - */ - public String toJson(JsonElement jsonElement) { - StringWriter writer = new StringWriter(); - toJson(jsonElement, writer); - return writer.toString(); - } - - /** - * Writes out the equivalent JSON for a tree of {@link JsonElement}s. - * - * @param jsonElement root of a tree of {@link JsonElement}s - * @param writer Writer to which the Json representation needs to be written - * @throws JsonIOException if there was a problem writing to the writer - * @since 1.4 - */ - public void toJson(JsonElement jsonElement, Appendable writer) throws JsonIOException { - try { - JsonWriter jsonWriter = newJsonWriter(Streams.writerForAppendable(writer)); - toJson(jsonElement, jsonWriter); - } catch (IOException e) { - throw new JsonIOException(e); - } - } - - /** - * Returns a new JSON writer configured for the settings on this Gson instance. - */ - public JsonWriter newJsonWriter(Writer writer) throws IOException { - if (generateNonExecutableJson) { - writer.write(JSON_NON_EXECUTABLE_PREFIX); - } - JsonWriter jsonWriter = new JsonWriter(writer); - if (prettyPrinting) { - jsonWriter.setIndent(" "); - } - jsonWriter.setSerializeNulls(serializeNulls); - return jsonWriter; - } - - /** - * Returns a new JSON reader configured for the settings on this Gson instance. - */ - public JsonReader newJsonReader(Reader reader) { - JsonReader jsonReader = new JsonReader(reader); - jsonReader.setLenient(lenient); - return jsonReader; - } - - /** - * Writes the JSON for {@code jsonElement} to {@code writer}. - * @throws JsonIOException if there was a problem writing to the writer - */ - public void toJson(JsonElement jsonElement, JsonWriter writer) throws JsonIOException { - boolean oldLenient = writer.isLenient(); - writer.setLenient(true); - boolean oldHtmlSafe = writer.isHtmlSafe(); - writer.setHtmlSafe(htmlSafe); - boolean oldSerializeNulls = writer.getSerializeNulls(); - writer.setSerializeNulls(serializeNulls); - try { - Streams.write(jsonElement, writer); - } catch (IOException e) { - throw new JsonIOException(e); - } finally { - writer.setLenient(oldLenient); - writer.setHtmlSafe(oldHtmlSafe); - writer.setSerializeNulls(oldSerializeNulls); - } - } - - /** - * This method deserializes the specified Json into an object of the specified class. It is not - * suitable to use if the specified class is a generic type since it will not have the generic - * type information because of the Type Erasure feature of Java. Therefore, this method should not - * be used if the desired type is a generic type. Note that this method works fine if the any of - * the fields of the specified object are generics, just the object itself should not be a - * generic type. For the cases when the object is of generic type, invoke - * {@link #fromJson(String, Type)}. If you have the Json in a {@link Reader} instead of - * a String, use {@link #fromJson(Reader, Class)} instead. - * - * @param the type of the desired object - * @param json the string from which the object is to be deserialized - * @param classOfT the class of T - * @return an object of type T from the string. Returns {@code null} if {@code json} is {@code null}. - * @throws JsonSyntaxException if json is not a valid representation for an object of type - * classOfT - */ - public T fromJson(String json, Class classOfT) throws JsonSyntaxException { - Object object = fromJson(json, (Type) classOfT); - return Primitives.wrap(classOfT).cast(object); - } - - /** - * This method deserializes the specified Json into an object of the specified type. This method - * is useful if the specified object is a generic type. For non-generic objects, use - * {@link #fromJson(String, Class)} instead. If you have the Json in a {@link Reader} instead of - * a String, use {@link #fromJson(Reader, Type)} instead. - * - * @param the type of the desired object - * @param json the string from which the object is to be deserialized - * @param typeOfT The specific genericized type of src. You can obtain this type by using the - * {@link com.google.gson.reflect.TypeToken} class. For example, to get the type for - * {@code Collection}, you should use: - *
-   * Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
-   * 
- * @return an object of type T from the string. Returns {@code null} if {@code json} is {@code null}. - * @throws JsonParseException if json is not a valid representation for an object of type typeOfT - * @throws JsonSyntaxException if json is not a valid representation for an object of type - */ - @SuppressWarnings("unchecked") - public T fromJson(String json, Type typeOfT) throws JsonSyntaxException { - if (json == null) { - return null; - } - StringReader reader = new StringReader(json); - T target = (T) fromJson(reader, typeOfT); - return target; - } - - /** - * This method deserializes the Json read from the specified reader into an object of the - * specified class. It is not suitable to use if the specified class is a generic type since it - * will not have the generic type information because of the Type Erasure feature of Java. - * Therefore, this method should not be used if the desired type is a generic type. Note that - * this method works fine if the any of the fields of the specified object are generics, just the - * object itself should not be a generic type. For the cases when the object is of generic type, - * invoke {@link #fromJson(Reader, Type)}. If you have the Json in a String form instead of a - * {@link Reader}, use {@link #fromJson(String, Class)} instead. - * - * @param the type of the desired object - * @param json the reader producing the Json from which the object is to be deserialized. - * @param classOfT the class of T - * @return an object of type T from the string. Returns {@code null} if {@code json} is at EOF. - * @throws JsonIOException if there was a problem reading from the Reader - * @throws JsonSyntaxException if json is not a valid representation for an object of type - * @since 1.2 - */ - public T fromJson(Reader json, Class classOfT) throws JsonSyntaxException, JsonIOException { - JsonReader jsonReader = newJsonReader(json); - Object object = fromJson(jsonReader, classOfT); - assertFullConsumption(object, jsonReader); - return Primitives.wrap(classOfT).cast(object); - } - - /** - * This method deserializes the Json read from the specified reader into an object of the - * specified type. This method is useful if the specified object is a generic type. For - * non-generic objects, use {@link #fromJson(Reader, Class)} instead. If you have the Json in a - * String form instead of a {@link Reader}, use {@link #fromJson(String, Type)} instead. - * - * @param the type of the desired object - * @param json the reader producing Json from which the object is to be deserialized - * @param typeOfT The specific genericized type of src. You can obtain this type by using the - * {@link com.google.gson.reflect.TypeToken} class. For example, to get the type for - * {@code Collection}, you should use: - *
-   * Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
-   * 
- * @return an object of type T from the json. Returns {@code null} if {@code json} is at EOF. - * @throws JsonIOException if there was a problem reading from the Reader - * @throws JsonSyntaxException if json is not a valid representation for an object of type - * @since 1.2 - */ - @SuppressWarnings("unchecked") - public T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException { - JsonReader jsonReader = newJsonReader(json); - T object = (T) fromJson(jsonReader, typeOfT); - assertFullConsumption(object, jsonReader); - return object; - } - - private static void assertFullConsumption(Object obj, JsonReader reader) { - try { - if (obj != null && reader.peek() != JsonToken.END_DOCUMENT) { - throw new JsonIOException("JSON document was not fully consumed."); - } - } catch (MalformedJsonException e) { - throw new JsonSyntaxException(e); - } catch (IOException e) { - throw new JsonIOException(e); - } - } - - /** - * Reads the next JSON value from {@code reader} and convert it to an object - * of type {@code typeOfT}. Returns {@code null}, if the {@code reader} is at EOF. - * Since Type is not parameterized by T, this method is type unsafe and should be used carefully - * - * @throws JsonIOException if there was a problem writing to the Reader - * @throws JsonSyntaxException if json is not a valid representation for an object of type - */ - @SuppressWarnings("unchecked") - public T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException { - boolean isEmpty = true; - boolean oldLenient = reader.isLenient(); - reader.setLenient(true); - try { - reader.peek(); - isEmpty = false; - TypeToken typeToken = (TypeToken) TypeToken.get(typeOfT); - TypeAdapter typeAdapter = getAdapter(typeToken); - T object = typeAdapter.read(reader); - return object; - } catch (EOFException e) { - /* - * For compatibility with JSON 1.5 and earlier, we return null for empty - * documents instead of throwing. - */ - if (isEmpty) { - return null; - } - throw new JsonSyntaxException(e); - } catch (IllegalStateException e) { - throw new JsonSyntaxException(e); - } catch (IOException e) { - // TODO(inder): Figure out whether it is indeed right to rethrow this as JsonSyntaxException - throw new JsonSyntaxException(e); - } finally { - reader.setLenient(oldLenient); - } - } - - /** - * This method deserializes the Json read from the specified parse tree into an object of the - * specified type. It is not suitable to use if the specified class is a generic type since it - * will not have the generic type information because of the Type Erasure feature of Java. - * Therefore, this method should not be used if the desired type is a generic type. Note that - * this method works fine if the any of the fields of the specified object are generics, just the - * object itself should not be a generic type. For the cases when the object is of generic type, - * invoke {@link #fromJson(JsonElement, Type)}. - * @param the type of the desired object - * @param json the root of the parse tree of {@link JsonElement}s from which the object is to - * be deserialized - * @param classOfT The class of T - * @return an object of type T from the json. Returns {@code null} if {@code json} is {@code null}. - * @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT - * @since 1.3 - */ - public T fromJson(JsonElement json, Class classOfT) throws JsonSyntaxException { - Object object = fromJson(json, (Type) classOfT); - return Primitives.wrap(classOfT).cast(object); - } - - /** - * This method deserializes the Json read from the specified parse tree into an object of the - * specified type. This method is useful if the specified object is a generic type. For - * non-generic objects, use {@link #fromJson(JsonElement, Class)} instead. - * - * @param the type of the desired object - * @param json the root of the parse tree of {@link JsonElement}s from which the object is to - * be deserialized - * @param typeOfT The specific genericized type of src. You can obtain this type by using the - * {@link com.google.gson.reflect.TypeToken} class. For example, to get the type for - * {@code Collection}, you should use: - *
-   * Type typeOfT = new TypeToken<Collection<Foo>>(){}.getType();
-   * 
- * @return an object of type T from the json. Returns {@code null} if {@code json} is {@code null}. - * @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT - * @since 1.3 - */ - @SuppressWarnings("unchecked") - public T fromJson(JsonElement json, Type typeOfT) throws JsonSyntaxException { - if (json == null) { - return null; - } - return (T) fromJson(new JsonTreeReader(json), typeOfT); - } - - static class FutureTypeAdapter extends TypeAdapter { - private TypeAdapter delegate; - - public void setDelegate(TypeAdapter typeAdapter) { - if (delegate != null) { - throw new AssertionError(); - } - delegate = typeAdapter; - } - - @Override public T read(JsonReader in) throws IOException { - if (delegate == null) { - throw new IllegalStateException(); - } - return delegate.read(in); - } - - @Override public void write(JsonWriter out, T value) throws IOException { - if (delegate == null) { - throw new IllegalStateException(); - } - delegate.write(out, value); - } - } - - @Override - public String toString() { - return new StringBuilder("{serializeNulls:") - .append(serializeNulls) - .append(",factories:").append(factories) - .append(",instanceCreators:").append(constructorConstructor) - .append("}") - .toString(); - } -} diff --git a/src/gson/main/java/com/google/gson/GsonBuilder.java b/src/gson/main/java/com/google/gson/GsonBuilder.java deleted file mode 100644 index e689386..0000000 --- a/src/gson/main/java/com/google/gson/GsonBuilder.java +++ /dev/null @@ -1,597 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -import java.lang.reflect.Type; -import java.sql.Timestamp; -import java.text.DateFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.google.gson.internal.$Gson$Preconditions; -import com.google.gson.internal.Excluder; -import com.google.gson.internal.bind.TreeTypeAdapter; -import com.google.gson.internal.bind.TypeAdapters; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; - -import static com.google.gson.Gson.DEFAULT_COMPLEX_MAP_KEYS; -import static com.google.gson.Gson.DEFAULT_ESCAPE_HTML; -import static com.google.gson.Gson.DEFAULT_JSON_NON_EXECUTABLE; -import static com.google.gson.Gson.DEFAULT_LENIENT; -import static com.google.gson.Gson.DEFAULT_PRETTY_PRINT; -import static com.google.gson.Gson.DEFAULT_SERIALIZE_NULLS; -import static com.google.gson.Gson.DEFAULT_SPECIALIZE_FLOAT_VALUES; - -/** - *

Use this builder to construct a {@link Gson} instance when you need to set configuration - * options other than the default. For {@link Gson} with default configuration, it is simpler to - * use {@code new Gson()}. {@code GsonBuilder} is best used by creating it, and then invoking its - * various configuration methods, and finally calling create.

- * - *

The following is an example shows how to use the {@code GsonBuilder} to construct a Gson - * instance: - * - *

- * Gson gson = new GsonBuilder()
- *     .registerTypeAdapter(Id.class, new IdTypeAdapter())
- *     .enableComplexMapKeySerialization()
- *     .serializeNulls()
- *     .setDateFormat(DateFormat.LONG)
- *     .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
- *     .setPrettyPrinting()
- *     .setVersion(1.0)
- *     .create();
- * 

- * - *

NOTES: - *

    - *
  • the order of invocation of configuration methods does not matter.
  • - *
  • The default serialization of {@link Date} and its subclasses in Gson does - * not contain time-zone information. So, if you are using date/time instances, - * use {@code GsonBuilder} and its {@code setDateFormat} methods.
  • - *
- *

- * - * @author Inderjeet Singh - * @author Joel Leitch - * @author Jesse Wilson - */ -public final class GsonBuilder { - private Excluder excluder = Excluder.DEFAULT; - private LongSerializationPolicy longSerializationPolicy = LongSerializationPolicy.DEFAULT; - private FieldNamingStrategy fieldNamingPolicy = FieldNamingPolicy.IDENTITY; - private final Map> instanceCreators - = new HashMap>(); - private final List factories = new ArrayList(); - /** tree-style hierarchy factories. These come after factories for backwards compatibility. */ - private final List hierarchyFactories = new ArrayList(); - private boolean serializeNulls = DEFAULT_SERIALIZE_NULLS; - private String datePattern; - private int dateStyle = DateFormat.DEFAULT; - private int timeStyle = DateFormat.DEFAULT; - private boolean complexMapKeySerialization = DEFAULT_COMPLEX_MAP_KEYS; - private boolean serializeSpecialFloatingPointValues = DEFAULT_SPECIALIZE_FLOAT_VALUES; - private boolean escapeHtmlChars = DEFAULT_ESCAPE_HTML; - private boolean prettyPrinting = DEFAULT_PRETTY_PRINT; - private boolean generateNonExecutableJson = DEFAULT_JSON_NON_EXECUTABLE; - private boolean lenient = DEFAULT_LENIENT; - - /** - * Creates a GsonBuilder instance that can be used to build Gson with various configuration - * settings. GsonBuilder follows the builder pattern, and it is typically used by first - * invoking various configuration methods to set desired options, and finally calling - * {@link #create()}. - */ - public GsonBuilder() { - } - - /** - * Configures Gson to enable versioning support. - * - * @param ignoreVersionsAfter any field or type marked with a version higher than this value - * are ignored during serialization or deserialization. - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - */ - public GsonBuilder setVersion(double ignoreVersionsAfter) { - excluder = excluder.withVersion(ignoreVersionsAfter); - return this; - } - - /** - * Configures Gson to excludes all class fields that have the specified modifiers. By default, - * Gson will exclude all fields marked transient or static. This method will override that - * behavior. - * - * @param modifiers the field modifiers. You must use the modifiers specified in the - * {@link java.lang.reflect.Modifier} class. For example, - * {@link java.lang.reflect.Modifier#TRANSIENT}, - * {@link java.lang.reflect.Modifier#STATIC}. - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - */ - public GsonBuilder excludeFieldsWithModifiers(int... modifiers) { - excluder = excluder.withModifiers(modifiers); - return this; - } - - /** - * Makes the output JSON non-executable in Javascript by prefixing the generated JSON with some - * special text. This prevents attacks from third-party sites through script sourcing. See - * Gson Issue 42 - * for details. - * - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - * @since 1.3 - */ - public GsonBuilder generateNonExecutableJson() { - this.generateNonExecutableJson = true; - return this; - } - - /** - * Configures Gson to exclude all fields from consideration for serialization or deserialization - * that do not have the {@link com.google.gson.annotations.Expose} annotation. - * - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - */ - public GsonBuilder excludeFieldsWithoutExposeAnnotation() { - excluder = excluder.excludeFieldsWithoutExposeAnnotation(); - return this; - } - - /** - * Configure Gson to serialize null fields. By default, Gson omits all fields that are null - * during serialization. - * - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - * @since 1.2 - */ - public GsonBuilder serializeNulls() { - this.serializeNulls = true; - return this; - } - - /** - * Enabling this feature will only change the serialized form if the map key is - * a complex type (i.e. non-primitive) in its serialized JSON - * form. The default implementation of map serialization uses {@code toString()} - * on the key; however, when this is called then one of the following cases - * apply: - * - *

Maps as JSON objects

- * For this case, assume that a type adapter is registered to serialize and - * deserialize some {@code Point} class, which contains an x and y coordinate, - * to/from the JSON Primitive string value {@code "(x,y)"}. The Java map would - * then be serialized as a {@link JsonObject}. - * - *

Below is an example: - *

  {@code
-   *   Gson gson = new GsonBuilder()
-   *       .register(Point.class, new MyPointTypeAdapter())
-   *       .enableComplexMapKeySerialization()
-   *       .create();
-   *
-   *   Map original = new LinkedHashMap();
-   *   original.put(new Point(5, 6), "a");
-   *   original.put(new Point(8, 8), "b");
-   *   System.out.println(gson.toJson(original, type));
-   * }
- * The above code prints this JSON object:
  {@code
-   *   {
-   *     "(5,6)": "a",
-   *     "(8,8)": "b"
-   *   }
-   * }
- * - *

Maps as JSON arrays

- * For this case, assume that a type adapter was NOT registered for some - * {@code Point} class, but rather the default Gson serialization is applied. - * In this case, some {@code new Point(2,3)} would serialize as {@code - * {"x":2,"y":5}}. - * - *

Given the assumption above, a {@code Map} will be - * serialize as an array of arrays (can be viewed as an entry set of pairs). - * - *

Below is an example of serializing complex types as JSON arrays: - *

 {@code
-   *   Gson gson = new GsonBuilder()
-   *       .enableComplexMapKeySerialization()
-   *       .create();
-   *
-   *   Map original = new LinkedHashMap();
-   *   original.put(new Point(5, 6), "a");
-   *   original.put(new Point(8, 8), "b");
-   *   System.out.println(gson.toJson(original, type));
-   * }
-   *
-   * The JSON output would look as follows:
-   * 
   {@code
-   *   [
-   *     [
-   *       {
-   *         "x": 5,
-   *         "y": 6
-   *       },
-   *       "a"
-   *     ],
-   *     [
-   *       {
-   *         "x": 8,
-   *         "y": 8
-   *       },
-   *       "b"
-   *     ]
-   *   ]
-   * }
- * - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - * @since 1.7 - */ - public GsonBuilder enableComplexMapKeySerialization() { - complexMapKeySerialization = true; - return this; - } - - /** - * Configures Gson to exclude inner classes during serialization. - * - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - * @since 1.3 - */ - public GsonBuilder disableInnerClassSerialization() { - excluder = excluder.disableInnerClassSerialization(); - return this; - } - - /** - * Configures Gson to apply a specific serialization policy for {@code Long} and {@code long} - * objects. - * - * @param serializationPolicy the particular policy to use for serializing longs. - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - * @since 1.3 - */ - public GsonBuilder setLongSerializationPolicy(LongSerializationPolicy serializationPolicy) { - this.longSerializationPolicy = serializationPolicy; - return this; - } - - /** - * Configures Gson to apply a specific naming policy to an object's field during serialization - * and deserialization. - * - * @param namingConvention the JSON field naming convention to use for serialization and - * deserialization. - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - */ - public GsonBuilder setFieldNamingPolicy(FieldNamingPolicy namingConvention) { - this.fieldNamingPolicy = namingConvention; - return this; - } - - /** - * Configures Gson to apply a specific naming policy strategy to an object's field during - * serialization and deserialization. - * - * @param fieldNamingStrategy the actual naming strategy to apply to the fields - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - * @since 1.3 - */ - public GsonBuilder setFieldNamingStrategy(FieldNamingStrategy fieldNamingStrategy) { - this.fieldNamingPolicy = fieldNamingStrategy; - return this; - } - - /** - * Configures Gson to apply a set of exclusion strategies during both serialization and - * deserialization. Each of the {@code strategies} will be applied as a disjunction rule. - * This means that if one of the {@code strategies} suggests that a field (or class) should be - * skipped then that field (or object) is skipped during serialization/deserialization. - * - * @param strategies the set of strategy object to apply during object (de)serialization. - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - * @since 1.4 - */ - public GsonBuilder setExclusionStrategies(ExclusionStrategy... strategies) { - for (ExclusionStrategy strategy : strategies) { - excluder = excluder.withExclusionStrategy(strategy, true, true); - } - return this; - } - - /** - * Configures Gson to apply the passed in exclusion strategy during serialization. - * If this method is invoked numerous times with different exclusion strategy objects - * then the exclusion strategies that were added will be applied as a disjunction rule. - * This means that if one of the added exclusion strategies suggests that a field (or - * class) should be skipped then that field (or object) is skipped during its - * serialization. - * - * @param strategy an exclusion strategy to apply during serialization. - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - * @since 1.7 - */ - public GsonBuilder addSerializationExclusionStrategy(ExclusionStrategy strategy) { - excluder = excluder.withExclusionStrategy(strategy, true, false); - return this; - } - - /** - * Configures Gson to apply the passed in exclusion strategy during deserialization. - * If this method is invoked numerous times with different exclusion strategy objects - * then the exclusion strategies that were added will be applied as a disjunction rule. - * This means that if one of the added exclusion strategies suggests that a field (or - * class) should be skipped then that field (or object) is skipped during its - * deserialization. - * - * @param strategy an exclusion strategy to apply during deserialization. - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - * @since 1.7 - */ - public GsonBuilder addDeserializationExclusionStrategy(ExclusionStrategy strategy) { - excluder = excluder.withExclusionStrategy(strategy, false, true); - return this; - } - - /** - * Configures Gson to output Json that fits in a page for pretty printing. This option only - * affects Json serialization. - * - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - */ - public GsonBuilder setPrettyPrinting() { - prettyPrinting = true; - return this; - } - - /** - * By default, Gson is strict and only accepts JSON as specified by - * RFC 4627. This option makes the parser - * liberal in what it accepts. - * - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - * @see JsonReader#setLenient(boolean) - */ - public GsonBuilder setLenient() { - lenient = true; - return this; - } - - /** - * By default, Gson escapes HTML characters such as < > etc. Use this option to configure - * Gson to pass-through HTML characters as is. - * - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - * @since 1.3 - */ - public GsonBuilder disableHtmlEscaping() { - this.escapeHtmlChars = false; - return this; - } - - /** - * Configures Gson to serialize {@code Date} objects according to the pattern provided. You can - * call this method or {@link #setDateFormat(int)} multiple times, but only the last invocation - * will be used to decide the serialization format. - * - *

The date format will be used to serialize and deserialize {@link java.util.Date}, {@link - * java.sql.Timestamp} and {@link java.sql.Date}. - * - *

Note that this pattern must abide by the convention provided by {@code SimpleDateFormat} - * class. See the documentation in {@link java.text.SimpleDateFormat} for more information on - * valid date and time patterns.

- * - * @param pattern the pattern that dates will be serialized/deserialized to/from - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - * @since 1.2 - */ - public GsonBuilder setDateFormat(String pattern) { - // TODO(Joel): Make this fail fast if it is an invalid date format - this.datePattern = pattern; - return this; - } - - /** - * Configures Gson to to serialize {@code Date} objects according to the style value provided. - * You can call this method or {@link #setDateFormat(String)} multiple times, but only the last - * invocation will be used to decide the serialization format. - * - *

Note that this style value should be one of the predefined constants in the - * {@code DateFormat} class. See the documentation in {@link java.text.DateFormat} for more - * information on the valid style constants.

- * - * @param style the predefined date style that date objects will be serialized/deserialized - * to/from - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - * @since 1.2 - */ - public GsonBuilder setDateFormat(int style) { - this.dateStyle = style; - this.datePattern = null; - return this; - } - - /** - * Configures Gson to to serialize {@code Date} objects according to the style value provided. - * You can call this method or {@link #setDateFormat(String)} multiple times, but only the last - * invocation will be used to decide the serialization format. - * - *

Note that this style value should be one of the predefined constants in the - * {@code DateFormat} class. See the documentation in {@link java.text.DateFormat} for more - * information on the valid style constants.

- * - * @param dateStyle the predefined date style that date objects will be serialized/deserialized - * to/from - * @param timeStyle the predefined style for the time portion of the date objects - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - * @since 1.2 - */ - public GsonBuilder setDateFormat(int dateStyle, int timeStyle) { - this.dateStyle = dateStyle; - this.timeStyle = timeStyle; - this.datePattern = null; - return this; - } - - /** - * Configures Gson for custom serialization or deserialization. This method combines the - * registration of an {@link TypeAdapter}, {@link InstanceCreator}, {@link JsonSerializer}, and a - * {@link JsonDeserializer}. It is best used when a single object {@code typeAdapter} implements - * all the required interfaces for custom serialization with Gson. If a type adapter was - * previously registered for the specified {@code type}, it is overwritten. - * - *

This registers the type specified and no other types: you must manually register related - * types! For example, applications registering {@code boolean.class} should also register {@code - * Boolean.class}. - * - * @param type the type definition for the type adapter being registered - * @param typeAdapter This object must implement at least one of the {@link TypeAdapter}, - * {@link InstanceCreator}, {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces. - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - */ - @SuppressWarnings({"unchecked", "rawtypes"}) - public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) { - $Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer - || typeAdapter instanceof JsonDeserializer - || typeAdapter instanceof InstanceCreator - || typeAdapter instanceof TypeAdapter); - if (typeAdapter instanceof InstanceCreator) { - instanceCreators.put(type, (InstanceCreator) typeAdapter); - } - if (typeAdapter instanceof JsonSerializer || typeAdapter instanceof JsonDeserializer) { - TypeToken typeToken = TypeToken.get(type); - factories.add(TreeTypeAdapter.newFactoryWithMatchRawType(typeToken, typeAdapter)); - } - if (typeAdapter instanceof TypeAdapter) { - factories.add(TypeAdapters.newFactory(TypeToken.get(type), (TypeAdapter)typeAdapter)); - } - return this; - } - - /** - * Register a factory for type adapters. Registering a factory is useful when the type - * adapter needs to be configured based on the type of the field being processed. Gson - * is designed to handle a large number of factories, so you should consider registering - * them to be at par with registering an individual type adapter. - * - * @since 2.1 - */ - public GsonBuilder registerTypeAdapterFactory(TypeAdapterFactory factory) { - factories.add(factory); - return this; - } - - /** - * Configures Gson for custom serialization or deserialization for an inheritance type hierarchy. - * This method combines the registration of a {@link TypeAdapter}, {@link JsonSerializer} and - * a {@link JsonDeserializer}. If a type adapter was previously registered for the specified - * type hierarchy, it is overridden. If a type adapter is registered for a specific type in - * the type hierarchy, it will be invoked instead of the one registered for the type hierarchy. - * - * @param baseType the class definition for the type adapter being registered for the base class - * or interface - * @param typeAdapter This object must implement at least one of {@link TypeAdapter}, - * {@link JsonSerializer} or {@link JsonDeserializer} interfaces. - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - * @since 1.7 - */ - @SuppressWarnings({"unchecked", "rawtypes"}) - public GsonBuilder registerTypeHierarchyAdapter(Class baseType, Object typeAdapter) { - $Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer - || typeAdapter instanceof JsonDeserializer - || typeAdapter instanceof TypeAdapter); - if (typeAdapter instanceof JsonDeserializer || typeAdapter instanceof JsonSerializer) { - hierarchyFactories.add(TreeTypeAdapter.newTypeHierarchyFactory(baseType, typeAdapter)); - } - if (typeAdapter instanceof TypeAdapter) { - factories.add(TypeAdapters.newTypeHierarchyFactory(baseType, (TypeAdapter)typeAdapter)); - } - return this; - } - - /** - * Section 2.4 of JSON specification disallows - * special double values (NaN, Infinity, -Infinity). However, - * Javascript - * specification (see section 4.3.20, 4.3.22, 4.3.23) allows these values as valid Javascript - * values. Moreover, most JavaScript engines will accept these special values in JSON without - * problem. So, at a practical level, it makes sense to accept these values as valid JSON even - * though JSON specification disallows them. - * - *

Gson always accepts these special values during deserialization. However, it outputs - * strictly compliant JSON. Hence, if it encounters a float value {@link Float#NaN}, - * {@link Float#POSITIVE_INFINITY}, {@link Float#NEGATIVE_INFINITY}, or a double value - * {@link Double#NaN}, {@link Double#POSITIVE_INFINITY}, {@link Double#NEGATIVE_INFINITY}, it - * will throw an {@link IllegalArgumentException}. This method provides a way to override the - * default behavior when you know that the JSON receiver will be able to handle these special - * values. - * - * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern - * @since 1.3 - */ - public GsonBuilder serializeSpecialFloatingPointValues() { - this.serializeSpecialFloatingPointValues = true; - return this; - } - - /** - * Creates a {@link Gson} instance based on the current configuration. This method is free of - * side-effects to this {@code GsonBuilder} instance and hence can be called multiple times. - * - * @return an instance of Gson configured with the options currently set in this builder - */ - public Gson create() { - List factories = new ArrayList(this.factories.size() + this.hierarchyFactories.size() + 3); - factories.addAll(this.factories); - Collections.reverse(factories); - Collections.reverse(this.hierarchyFactories); - factories.addAll(this.hierarchyFactories); - addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, factories); - - return new Gson(excluder, fieldNamingPolicy, instanceCreators, - serializeNulls, complexMapKeySerialization, - generateNonExecutableJson, escapeHtmlChars, prettyPrinting, lenient, - serializeSpecialFloatingPointValues, longSerializationPolicy, factories); - } - - @SuppressWarnings("unchecked") - private void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle, - List factories) { - DefaultDateTypeAdapter dateTypeAdapter; - TypeAdapter timestampTypeAdapter; - TypeAdapter javaSqlDateTypeAdapter; - if (datePattern != null && !"".equals(datePattern.trim())) { - dateTypeAdapter = new DefaultDateTypeAdapter(Date.class, datePattern); - timestampTypeAdapter = (TypeAdapter) new DefaultDateTypeAdapter(Timestamp.class, datePattern); - javaSqlDateTypeAdapter = (TypeAdapter) new DefaultDateTypeAdapter(java.sql.Date.class, datePattern); - } else if (dateStyle != DateFormat.DEFAULT && timeStyle != DateFormat.DEFAULT) { - dateTypeAdapter = new DefaultDateTypeAdapter(Date.class, dateStyle, timeStyle); - timestampTypeAdapter = (TypeAdapter) new DefaultDateTypeAdapter(Timestamp.class, dateStyle, timeStyle); - javaSqlDateTypeAdapter = (TypeAdapter) new DefaultDateTypeAdapter(java.sql.Date.class, dateStyle, timeStyle); - } else { - return; - } - - factories.add(TypeAdapters.newFactory(Date.class, dateTypeAdapter)); - factories.add(TypeAdapters.newFactory(Timestamp.class, timestampTypeAdapter)); - factories.add(TypeAdapters.newFactory(java.sql.Date.class, javaSqlDateTypeAdapter)); - } -} diff --git a/src/gson/main/java/com/google/gson/InstanceCreator.java b/src/gson/main/java/com/google/gson/InstanceCreator.java deleted file mode 100644 index d5096a0..0000000 --- a/src/gson/main/java/com/google/gson/InstanceCreator.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -import java.lang.reflect.Type; - -/** - * This interface is implemented to create instances of a class that does not define a no-args - * constructor. If you can modify the class, you should instead add a private, or public - * no-args constructor. However, that is not possible for library classes, such as JDK classes, or - * a third-party library that you do not have source-code of. In such cases, you should define an - * instance creator for the class. Implementations of this interface should be registered with - * {@link GsonBuilder#registerTypeAdapter(Type, Object)} method before Gson will be able to use - * them. - *

Let us look at an example where defining an InstanceCreator might be useful. The - * {@code Id} class defined below does not have a default no-args constructor.

- * - *
- * public class Id<T> {
- *   private final Class<T> clazz;
- *   private final long value;
- *   public Id(Class<T> clazz, long value) {
- *     this.clazz = clazz;
- *     this.value = value;
- *   }
- * }
- * 
- * - *

If Gson encounters an object of type {@code Id} during deserialization, it will throw an - * exception. The easiest way to solve this problem will be to add a (public or private) no-args - * constructor as follows:

- * - *
- * private Id() {
- *   this(Object.class, 0L);
- * }
- * 
- * - *

However, let us assume that the developer does not have access to the source-code of the - * {@code Id} class, or does not want to define a no-args constructor for it. The developer - * can solve this problem by defining an {@code InstanceCreator} for {@code Id}:

- * - *
- * class IdInstanceCreator implements InstanceCreator<Id> {
- *   public Id createInstance(Type type) {
- *     return new Id(Object.class, 0L);
- *   }
- * }
- * 
- * - *

Note that it does not matter what the fields of the created instance contain since Gson will - * overwrite them with the deserialized values specified in Json. You should also ensure that a - * new object is returned, not a common object since its fields will be overwritten. - * The developer will need to register {@code IdInstanceCreator} with Gson as follows:

- * - *
- * Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdInstanceCreator()).create();
- * 
- * - * @param the type of object that will be created by this implementation. - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -public interface InstanceCreator { - - /** - * Gson invokes this call-back method during deserialization to create an instance of the - * specified type. The fields of the returned instance are overwritten with the data present - * in the Json. Since the prior contents of the object are destroyed and overwritten, do not - * return an instance that is useful elsewhere. In particular, do not return a common instance, - * always use {@code new} to create a new instance. - * - * @param type the parameterized T represented as a {@link Type}. - * @return a default object instance of type T. - */ - public T createInstance(Type type); -} diff --git a/src/gson/main/java/com/google/gson/JsonArray.java b/src/gson/main/java/com/google/gson/JsonArray.java deleted file mode 100644 index f2b3b31..0000000 --- a/src/gson/main/java/com/google/gson/JsonArray.java +++ /dev/null @@ -1,384 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -/** - * A class representing an array type in Json. An array is a list of {@link JsonElement}s each of - * which can be of a different type. This is an ordered list, meaning that the order in which - * elements are added is preserved. - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -public final class JsonArray extends JsonElement implements Iterable { - private final List elements; - - /** - * Creates an empty JsonArray. - */ - public JsonArray() { - elements = new ArrayList(); - } - - public JsonArray(int capacity) { - elements = new ArrayList(capacity); - } - - /** - * Creates a deep copy of this element and all its children - * @since 2.8.2 - */ - @Override - public JsonArray deepCopy() { - if (!elements.isEmpty()) { - JsonArray result = new JsonArray(elements.size()); - for (JsonElement element : elements) { - result.add(element.deepCopy()); - } - return result; - } - return new JsonArray(); - } - - /** - * Adds the specified boolean to self. - * - * @param bool the boolean that needs to be added to the array. - */ - public void add(Boolean bool) { - elements.add(bool == null ? JsonNull.INSTANCE : new JsonPrimitive(bool)); - } - - /** - * Adds the specified character to self. - * - * @param character the character that needs to be added to the array. - */ - public void add(Character character) { - elements.add(character == null ? JsonNull.INSTANCE : new JsonPrimitive(character)); - } - - /** - * Adds the specified number to self. - * - * @param number the number that needs to be added to the array. - */ - public void add(Number number) { - elements.add(number == null ? JsonNull.INSTANCE : new JsonPrimitive(number)); - } - - /** - * Adds the specified string to self. - * - * @param string the string that needs to be added to the array. - */ - public void add(String string) { - elements.add(string == null ? JsonNull.INSTANCE : new JsonPrimitive(string)); - } - - /** - * Adds the specified element to self. - * - * @param element the element that needs to be added to the array. - */ - public void add(JsonElement element) { - if (element == null) { - element = JsonNull.INSTANCE; - } - elements.add(element); - } - - /** - * Adds all the elements of the specified array to self. - * - * @param array the array whose elements need to be added to the array. - */ - public void addAll(JsonArray array) { - elements.addAll(array.elements); - } - - /** - * Replaces the element at the specified position in this array with the specified element. - * Element can be null. - * @param index index of the element to replace - * @param element element to be stored at the specified position - * @return the element previously at the specified position - * @throws IndexOutOfBoundsException if the specified index is outside the array bounds - */ - public JsonElement set(int index, JsonElement element) { - return elements.set(index, element); - } - - /** - * Removes the first occurrence of the specified element from this array, if it is present. - * If the array does not contain the element, it is unchanged. - * @param element element to be removed from this array, if present - * @return true if this array contained the specified element, false otherwise - * @since 2.3 - */ - public boolean remove(JsonElement element) { - return elements.remove(element); - } - - /** - * Removes the element at the specified position in this array. Shifts any subsequent elements - * to the left (subtracts one from their indices). Returns the element that was removed from - * the array. - * @param index index the index of the element to be removed - * @return the element previously at the specified position - * @throws IndexOutOfBoundsException if the specified index is outside the array bounds - * @since 2.3 - */ - public JsonElement remove(int index) { - return elements.remove(index); - } - - /** - * Returns true if this array contains the specified element. - * @return true if this array contains the specified element. - * @param element whose presence in this array is to be tested - * @since 2.3 - */ - public boolean contains(JsonElement element) { - return elements.contains(element); - } - - /** - * Returns the number of elements in the array. - * - * @return the number of elements in the array. - */ - public int size() { - return elements.size(); - } - - /** - * Returns an iterator to navigate the elements of the array. Since the array is an ordered list, - * the iterator navigates the elements in the order they were inserted. - * - * @return an iterator to navigate the elements of the array. - */ - public Iterator iterator() { - return elements.iterator(); - } - - /** - * Returns the ith element of the array. - * - * @param i the index of the element that is being sought. - * @return the element present at the ith index. - * @throws IndexOutOfBoundsException if i is negative or greater than or equal to the - * {@link #size()} of the array. - */ - public JsonElement get(int i) { - return elements.get(i); - } - - /** - * convenience method to get this array as a {@link Number} if it contains a single element. - * - * @return get this element as a number if it is single element array. - * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and - * is not a valid Number. - * @throws IllegalStateException if the array has more than one element. - */ - @Override - public Number getAsNumber() { - if (elements.size() == 1) { - return elements.get(0).getAsNumber(); - } - throw new IllegalStateException(); - } - - /** - * convenience method to get this array as a {@link String} if it contains a single element. - * - * @return get this element as a String if it is single element array. - * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and - * is not a valid String. - * @throws IllegalStateException if the array has more than one element. - */ - @Override - public String getAsString() { - if (elements.size() == 1) { - return elements.get(0).getAsString(); - } - throw new IllegalStateException(); - } - - /** - * convenience method to get this array as a double if it contains a single element. - * - * @return get this element as a double if it is single element array. - * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and - * is not a valid double. - * @throws IllegalStateException if the array has more than one element. - */ - @Override - public double getAsDouble() { - if (elements.size() == 1) { - return elements.get(0).getAsDouble(); - } - throw new IllegalStateException(); - } - - /** - * convenience method to get this array as a {@link BigDecimal} if it contains a single element. - * - * @return get this element as a {@link BigDecimal} if it is single element array. - * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive}. - * @throws NumberFormatException if the element at index 0 is not a valid {@link BigDecimal}. - * @throws IllegalStateException if the array has more than one element. - * @since 1.2 - */ - @Override - public BigDecimal getAsBigDecimal() { - if (elements.size() == 1) { - return elements.get(0).getAsBigDecimal(); - } - throw new IllegalStateException(); - } - - /** - * convenience method to get this array as a {@link BigInteger} if it contains a single element. - * - * @return get this element as a {@link BigInteger} if it is single element array. - * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive}. - * @throws NumberFormatException if the element at index 0 is not a valid {@link BigInteger}. - * @throws IllegalStateException if the array has more than one element. - * @since 1.2 - */ - @Override - public BigInteger getAsBigInteger() { - if (elements.size() == 1) { - return elements.get(0).getAsBigInteger(); - } - throw new IllegalStateException(); - } - - /** - * convenience method to get this array as a float if it contains a single element. - * - * @return get this element as a float if it is single element array. - * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and - * is not a valid float. - * @throws IllegalStateException if the array has more than one element. - */ - @Override - public float getAsFloat() { - if (elements.size() == 1) { - return elements.get(0).getAsFloat(); - } - throw new IllegalStateException(); - } - - /** - * convenience method to get this array as a long if it contains a single element. - * - * @return get this element as a long if it is single element array. - * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and - * is not a valid long. - * @throws IllegalStateException if the array has more than one element. - */ - @Override - public long getAsLong() { - if (elements.size() == 1) { - return elements.get(0).getAsLong(); - } - throw new IllegalStateException(); - } - - /** - * convenience method to get this array as an integer if it contains a single element. - * - * @return get this element as an integer if it is single element array. - * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and - * is not a valid integer. - * @throws IllegalStateException if the array has more than one element. - */ - @Override - public int getAsInt() { - if (elements.size() == 1) { - return elements.get(0).getAsInt(); - } - throw new IllegalStateException(); - } - - @Override - public byte getAsByte() { - if (elements.size() == 1) { - return elements.get(0).getAsByte(); - } - throw new IllegalStateException(); - } - - @Override - public char getAsCharacter() { - if (elements.size() == 1) { - return elements.get(0).getAsCharacter(); - } - throw new IllegalStateException(); - } - - /** - * convenience method to get this array as a primitive short if it contains a single element. - * - * @return get this element as a primitive short if it is single element array. - * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and - * is not a valid short. - * @throws IllegalStateException if the array has more than one element. - */ - @Override - public short getAsShort() { - if (elements.size() == 1) { - return elements.get(0).getAsShort(); - } - throw new IllegalStateException(); - } - - /** - * convenience method to get this array as a boolean if it contains a single element. - * - * @return get this element as a boolean if it is single element array. - * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and - * is not a valid boolean. - * @throws IllegalStateException if the array has more than one element. - */ - @Override - public boolean getAsBoolean() { - if (elements.size() == 1) { - return elements.get(0).getAsBoolean(); - } - throw new IllegalStateException(); - } - - @Override - public boolean equals(Object o) { - return (o == this) || (o instanceof JsonArray && ((JsonArray) o).elements.equals(elements)); - } - - @Override - public int hashCode() { - return elements.hashCode(); - } -} diff --git a/src/gson/main/java/com/google/gson/JsonDeserializationContext.java b/src/gson/main/java/com/google/gson/JsonDeserializationContext.java deleted file mode 100644 index 00c7505..0000000 --- a/src/gson/main/java/com/google/gson/JsonDeserializationContext.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -import java.lang.reflect.Type; - -/** - * Context for deserialization that is passed to a custom deserializer during invocation of its - * {@link JsonDeserializer#deserialize(JsonElement, Type, JsonDeserializationContext)} - * method. - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -public interface JsonDeserializationContext { - - /** - * Invokes default deserialization on the specified object. It should never be invoked on - * the element received as a parameter of the - * {@link JsonDeserializer#deserialize(JsonElement, Type, JsonDeserializationContext)} method. Doing - * so will result in an infinite loop since Gson will in-turn call the custom deserializer again. - * - * @param json the parse tree. - * @param typeOfT type of the expected return value. - * @param The type of the deserialized object. - * @return An object of type typeOfT. - * @throws JsonParseException if the parse tree does not contain expected data. - */ - public T deserialize(JsonElement json, Type typeOfT) throws JsonParseException; -} \ No newline at end of file diff --git a/src/gson/main/java/com/google/gson/JsonDeserializer.java b/src/gson/main/java/com/google/gson/JsonDeserializer.java deleted file mode 100644 index 0589eb2..0000000 --- a/src/gson/main/java/com/google/gson/JsonDeserializer.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -import java.lang.reflect.Type; - -/** - *

Interface representing a custom deserializer for Json. You should write a custom - * deserializer, if you are not happy with the default deserialization done by Gson. You will - * also need to register this deserializer through - * {@link GsonBuilder#registerTypeAdapter(Type, Object)}.

- * - *

Let us look at example where defining a deserializer will be useful. The {@code Id} class - * defined below has two fields: {@code clazz} and {@code value}.

- * - *
- * public class Id<T> {
- *   private final Class<T> clazz;
- *   private final long value;
- *   public Id(Class<T> clazz, long value) {
- *     this.clazz = clazz;
- *     this.value = value;
- *   }
- *   public long getValue() {
- *     return value;
- *   }
- * }
- * 
- * - *

The default deserialization of {@code Id(com.foo.MyObject.class, 20L)} will require the - * Json string to be {"clazz":com.foo.MyObject,"value":20}. Suppose, you already know - * the type of the field that the {@code Id} will be deserialized into, and hence just want to - * deserialize it from a Json string {@code 20}. You can achieve that by writing a custom - * deserializer:

- * - *
- * class IdDeserializer implements JsonDeserializer<Id>() {
- *   public Id deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
- *       throws JsonParseException {
- *     return new Id((Class)typeOfT, id.getValue());
- *   }
- * 
- * - *

You will also need to register {@code IdDeserializer} with Gson as follows:

- * - *
- * Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdDeserializer()).create();
- * 
- * - *

New applications should prefer {@link TypeAdapter}, whose streaming API - * is more efficient than this interface's tree API. - * - * @author Inderjeet Singh - * @author Joel Leitch - * - * @param type for which the deserializer is being registered. It is possible that a - * deserializer may be asked to deserialize a specific generic type of the T. - */ -public interface JsonDeserializer { - - /** - * Gson invokes this call-back method during deserialization when it encounters a field of the - * specified type. - *

In the implementation of this call-back method, you should consider invoking - * {@link JsonDeserializationContext#deserialize(JsonElement, Type)} method to create objects - * for any non-trivial field of the returned object. However, you should never invoke it on the - * the same type passing {@code json} since that will cause an infinite loop (Gson will call your - * call-back method again). - * - * @param json The Json data being deserialized - * @param typeOfT The type of the Object to deserialize to - * @return a deserialized object of the specified type typeOfT which is a subclass of {@code T} - * @throws JsonParseException if json is not in the expected format of {@code typeofT} - */ - public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException; -} diff --git a/src/gson/main/java/com/google/gson/JsonElement.java b/src/gson/main/java/com/google/gson/JsonElement.java deleted file mode 100644 index 62bb920..0000000 --- a/src/gson/main/java/com/google/gson/JsonElement.java +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -import com.google.gson.internal.Streams; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.io.StringWriter; -import java.math.BigDecimal; -import java.math.BigInteger; - -/** - * A class representing an element of Json. It could either be a {@link JsonObject}, a - * {@link JsonArray}, a {@link JsonPrimitive} or a {@link JsonNull}. - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -public abstract class JsonElement { - /** - * Returns a deep copy of this element. Immutable elements like primitives - * and nulls are not copied. - * @since 2.8.2 - */ - public abstract JsonElement deepCopy(); - - /** - * provides check for verifying if this element is an array or not. - * - * @return true if this element is of type {@link JsonArray}, false otherwise. - */ - public boolean isJsonArray() { - return this instanceof JsonArray; - } - - /** - * provides check for verifying if this element is a Json object or not. - * - * @return true if this element is of type {@link JsonObject}, false otherwise. - */ - public boolean isJsonObject() { - return this instanceof JsonObject; - } - - /** - * provides check for verifying if this element is a primitive or not. - * - * @return true if this element is of type {@link JsonPrimitive}, false otherwise. - */ - public boolean isJsonPrimitive() { - return this instanceof JsonPrimitive; - } - - /** - * provides check for verifying if this element represents a null value or not. - * - * @return true if this element is of type {@link JsonNull}, false otherwise. - * @since 1.2 - */ - public boolean isJsonNull() { - return this instanceof JsonNull; - } - - /** - * convenience method to get this element as a {@link JsonObject}. If the element is of some - * other type, a {@link IllegalStateException} will result. Hence it is best to use this method - * after ensuring that this element is of the desired type by calling {@link #isJsonObject()} - * first. - * - * @return get this element as a {@link JsonObject}. - * @throws IllegalStateException if the element is of another type. - */ - public JsonObject getAsJsonObject() { - if (isJsonObject()) { - return (JsonObject) this; - } - throw new IllegalStateException("Not a JSON Object: " + this); - } - - /** - * convenience method to get this element as a {@link JsonArray}. If the element is of some - * other type, a {@link IllegalStateException} will result. Hence it is best to use this method - * after ensuring that this element is of the desired type by calling {@link #isJsonArray()} - * first. - * - * @return get this element as a {@link JsonArray}. - * @throws IllegalStateException if the element is of another type. - */ - public JsonArray getAsJsonArray() { - if (isJsonArray()) { - return (JsonArray) this; - } - throw new IllegalStateException("Not a JSON Array: " + this); - } - - /** - * convenience method to get this element as a {@link JsonPrimitive}. If the element is of some - * other type, a {@link IllegalStateException} will result. Hence it is best to use this method - * after ensuring that this element is of the desired type by calling {@link #isJsonPrimitive()} - * first. - * - * @return get this element as a {@link JsonPrimitive}. - * @throws IllegalStateException if the element is of another type. - */ - public JsonPrimitive getAsJsonPrimitive() { - if (isJsonPrimitive()) { - return (JsonPrimitive) this; - } - throw new IllegalStateException("Not a JSON Primitive: " + this); - } - - /** - * convenience method to get this element as a {@link JsonNull}. If the element is of some - * other type, a {@link IllegalStateException} will result. Hence it is best to use this method - * after ensuring that this element is of the desired type by calling {@link #isJsonNull()} - * first. - * - * @return get this element as a {@link JsonNull}. - * @throws IllegalStateException if the element is of another type. - * @since 1.2 - */ - public JsonNull getAsJsonNull() { - if (isJsonNull()) { - return (JsonNull) this; - } - throw new IllegalStateException("Not a JSON Null: " + this); - } - - /** - * convenience method to get this element as a boolean value. - * - * @return get this element as a primitive boolean value. - * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid - * boolean value. - * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains - * more than a single element. - */ - public boolean getAsBoolean() { - throw new UnsupportedOperationException(getClass().getSimpleName()); - } - - /** - * convenience method to get this element as a {@link Boolean} value. - * - * @return get this element as a {@link Boolean} value. - * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid - * boolean value. - * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains - * more than a single element. - */ - Boolean getAsBooleanWrapper() { - throw new UnsupportedOperationException(getClass().getSimpleName()); - } - - /** - * convenience method to get this element as a {@link Number}. - * - * @return get this element as a {@link Number}. - * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid - * number. - * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains - * more than a single element. - */ - public Number getAsNumber() { - throw new UnsupportedOperationException(getClass().getSimpleName()); - } - - /** - * convenience method to get this element as a string value. - * - * @return get this element as a string value. - * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid - * string value. - * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains - * more than a single element. - */ - public String getAsString() { - throw new UnsupportedOperationException(getClass().getSimpleName()); - } - - /** - * convenience method to get this element as a primitive double value. - * - * @return get this element as a primitive double value. - * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid - * double value. - * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains - * more than a single element. - */ - public double getAsDouble() { - throw new UnsupportedOperationException(getClass().getSimpleName()); - } - - /** - * convenience method to get this element as a primitive float value. - * - * @return get this element as a primitive float value. - * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid - * float value. - * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains - * more than a single element. - */ - public float getAsFloat() { - throw new UnsupportedOperationException(getClass().getSimpleName()); - } - - /** - * convenience method to get this element as a primitive long value. - * - * @return get this element as a primitive long value. - * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid - * long value. - * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains - * more than a single element. - */ - public long getAsLong() { - throw new UnsupportedOperationException(getClass().getSimpleName()); - } - - /** - * convenience method to get this element as a primitive integer value. - * - * @return get this element as a primitive integer value. - * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid - * integer value. - * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains - * more than a single element. - */ - public int getAsInt() { - throw new UnsupportedOperationException(getClass().getSimpleName()); - } - - /** - * convenience method to get this element as a primitive byte value. - * - * @return get this element as a primitive byte value. - * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid - * byte value. - * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains - * more than a single element. - * @since 1.3 - */ - public byte getAsByte() { - throw new UnsupportedOperationException(getClass().getSimpleName()); - } - - /** - * convenience method to get this element as a primitive character value. - * - * @return get this element as a primitive char value. - * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid - * char value. - * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains - * more than a single element. - * @since 1.3 - */ - public char getAsCharacter() { - throw new UnsupportedOperationException(getClass().getSimpleName()); - } - - /** - * convenience method to get this element as a {@link BigDecimal}. - * - * @return get this element as a {@link BigDecimal}. - * @throws ClassCastException if the element is of not a {@link JsonPrimitive}. - * * @throws NumberFormatException if the element is not a valid {@link BigDecimal}. - * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains - * more than a single element. - * @since 1.2 - */ - public BigDecimal getAsBigDecimal() { - throw new UnsupportedOperationException(getClass().getSimpleName()); - } - - /** - * convenience method to get this element as a {@link BigInteger}. - * - * @return get this element as a {@link BigInteger}. - * @throws ClassCastException if the element is of not a {@link JsonPrimitive}. - * @throws NumberFormatException if the element is not a valid {@link BigInteger}. - * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains - * more than a single element. - * @since 1.2 - */ - public BigInteger getAsBigInteger() { - throw new UnsupportedOperationException(getClass().getSimpleName()); - } - - /** - * convenience method to get this element as a primitive short value. - * - * @return get this element as a primitive short value. - * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid - * short value. - * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains - * more than a single element. - */ - public short getAsShort() { - throw new UnsupportedOperationException(getClass().getSimpleName()); - } - - /** - * Returns a String representation of this element. - */ - @Override - public String toString() { - try { - StringWriter stringWriter = new StringWriter(); - JsonWriter jsonWriter = new JsonWriter(stringWriter); - jsonWriter.setLenient(true); - Streams.write(this, jsonWriter); - return stringWriter.toString(); - } catch (IOException e) { - throw new AssertionError(e); - } - } -} diff --git a/src/gson/main/java/com/google/gson/JsonIOException.java b/src/gson/main/java/com/google/gson/JsonIOException.java deleted file mode 100644 index dfeccd8..0000000 --- a/src/gson/main/java/com/google/gson/JsonIOException.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.gson; - -/** - * This exception is raised when Gson was unable to read an input stream - * or write to one. - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -public final class JsonIOException extends JsonParseException { - private static final long serialVersionUID = 1L; - - public JsonIOException(String msg) { - super(msg); - } - - public JsonIOException(String msg, Throwable cause) { - super(msg, cause); - } - - /** - * Creates exception with the specified cause. Consider using - * {@link #JsonIOException(String, Throwable)} instead if you can describe what happened. - * - * @param cause root exception that caused this exception to be thrown. - */ - public JsonIOException(Throwable cause) { - super(cause); - } -} diff --git a/src/gson/main/java/com/google/gson/JsonNull.java b/src/gson/main/java/com/google/gson/JsonNull.java deleted file mode 100644 index dcc10a7..0000000 --- a/src/gson/main/java/com/google/gson/JsonNull.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -/** - * A class representing a Json {@code null} value. - * - * @author Inderjeet Singh - * @author Joel Leitch - * @since 1.2 - */ -public final class JsonNull extends JsonElement { - /** - * singleton for JsonNull - * - * @since 1.8 - */ - public static final JsonNull INSTANCE = new JsonNull(); - - /** - * Creates a new JsonNull object. - * Deprecated since Gson version 1.8. Use {@link #INSTANCE} instead - */ - @Deprecated - public JsonNull() { - // Do nothing - } - - /** - * Returns the same instance since it is an immutable value - * @since 2.8.2 - */ - @Override - public JsonNull deepCopy() { - return INSTANCE; - } - - /** - * All instances of JsonNull have the same hash code since they are indistinguishable - */ - @Override - public int hashCode() { - return JsonNull.class.hashCode(); - } - - /** - * All instances of JsonNull are the same - */ - @Override - public boolean equals(Object other) { - return this == other || other instanceof JsonNull; - } -} diff --git a/src/gson/main/java/com/google/gson/JsonObject.java b/src/gson/main/java/com/google/gson/JsonObject.java deleted file mode 100644 index 4b63db5..0000000 --- a/src/gson/main/java/com/google/gson/JsonObject.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -import com.google.gson.internal.LinkedTreeMap; - -import java.util.Map; -import java.util.Set; - -/** - * A class representing an object type in Json. An object consists of name-value pairs where names - * are strings, and values are any other type of {@link JsonElement}. This allows for a creating a - * tree of JsonElements. The member elements of this object are maintained in order they were added. - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -public final class JsonObject extends JsonElement { - private final LinkedTreeMap members = - new LinkedTreeMap(); - - /** - * Creates a deep copy of this element and all its children - * @since 2.8.2 - */ - @Override - public JsonObject deepCopy() { - JsonObject result = new JsonObject(); - for (Map.Entry entry : members.entrySet()) { - result.add(entry.getKey(), entry.getValue().deepCopy()); - } - return result; - } - - /** - * Adds a member, which is a name-value pair, to self. The name must be a String, but the value - * can be an arbitrary JsonElement, thereby allowing you to build a full tree of JsonElements - * rooted at this node. - * - * @param property name of the member. - * @param value the member object. - */ - public void add(String property, JsonElement value) { - if (value == null) { - value = JsonNull.INSTANCE; - } - members.put(property, value); - } - - /** - * Removes the {@code property} from this {@link JsonObject}. - * - * @param property name of the member that should be removed. - * @return the {@link JsonElement} object that is being removed. - * @since 1.3 - */ - public JsonElement remove(String property) { - return members.remove(property); - } - - /** - * Convenience method to add a primitive member. The specified value is converted to a - * JsonPrimitive of String. - * - * @param property name of the member. - * @param value the string value associated with the member. - */ - public void addProperty(String property, String value) { - add(property, createJsonElement(value)); - } - - /** - * Convenience method to add a primitive member. The specified value is converted to a - * JsonPrimitive of Number. - * - * @param property name of the member. - * @param value the number value associated with the member. - */ - public void addProperty(String property, Number value) { - add(property, createJsonElement(value)); - } - - /** - * Convenience method to add a boolean member. The specified value is converted to a - * JsonPrimitive of Boolean. - * - * @param property name of the member. - * @param value the number value associated with the member. - */ - public void addProperty(String property, Boolean value) { - add(property, createJsonElement(value)); - } - - /** - * Convenience method to add a char member. The specified value is converted to a - * JsonPrimitive of Character. - * - * @param property name of the member. - * @param value the number value associated with the member. - */ - public void addProperty(String property, Character value) { - add(property, createJsonElement(value)); - } - - /** - * Creates the proper {@link JsonElement} object from the given {@code value} object. - * - * @param value the object to generate the {@link JsonElement} for - * @return a {@link JsonPrimitive} if the {@code value} is not null, otherwise a {@link JsonNull} - */ - private JsonElement createJsonElement(Object value) { - return value == null ? JsonNull.INSTANCE : new JsonPrimitive(value); - } - - /** - * Returns a set of members of this object. The set is ordered, and the order is in which the - * elements were added. - * - * @return a set of members of this object. - */ - public Set> entrySet() { - return members.entrySet(); - } - - /** - * Returns a set of members key values. - * - * @return a set of member keys as Strings - * @since 2.8.1 - */ - public Set keySet() { - return members.keySet(); - } - - /** - * Returns the number of key/value pairs in the object. - * - * @return the number of key/value pairs in the object. - */ - public int size() { - return members.size(); - } - - /** - * Convenience method to check if a member with the specified name is present in this object. - * - * @param memberName name of the member that is being checked for presence. - * @return true if there is a member with the specified name, false otherwise. - */ - public boolean has(String memberName) { - return members.containsKey(memberName); - } - - /** - * Returns the member with the specified name. - * - * @param memberName name of the member that is being requested. - * @return the member matching the name. Null if no such member exists. - */ - public JsonElement get(String memberName) { - return members.get(memberName); - } - - /** - * Convenience method to get the specified member as a JsonPrimitive element. - * - * @param memberName name of the member being requested. - * @return the JsonPrimitive corresponding to the specified member. - */ - public JsonPrimitive getAsJsonPrimitive(String memberName) { - return (JsonPrimitive) members.get(memberName); - } - - /** - * Convenience method to get the specified member as a JsonArray. - * - * @param memberName name of the member being requested. - * @return the JsonArray corresponding to the specified member. - */ - public JsonArray getAsJsonArray(String memberName) { - return (JsonArray) members.get(memberName); - } - - /** - * Convenience method to get the specified member as a JsonObject. - * - * @param memberName name of the member being requested. - * @return the JsonObject corresponding to the specified member. - */ - public JsonObject getAsJsonObject(String memberName) { - return (JsonObject) members.get(memberName); - } - - @Override - public boolean equals(Object o) { - return (o == this) || (o instanceof JsonObject - && ((JsonObject) o).members.equals(members)); - } - - @Override - public int hashCode() { - return members.hashCode(); - } -} diff --git a/src/gson/main/java/com/google/gson/JsonParseException.java b/src/gson/main/java/com/google/gson/JsonParseException.java deleted file mode 100644 index 084f661..0000000 --- a/src/gson/main/java/com/google/gson/JsonParseException.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -/** - * This exception is raised if there is a serious issue that occurs during parsing of a Json - * string. One of the main usages for this class is for the Gson infrastructure. If the incoming - * Json is bad/malicious, an instance of this exception is raised. - * - *

This exception is a {@link RuntimeException} because it is exposed to the client. Using a - * {@link RuntimeException} avoids bad coding practices on the client side where they catch the - * exception and do nothing. It is often the case that you want to blow up if there is a parsing - * error (i.e. often clients do not know how to recover from a {@link JsonParseException}.

- * - * @author Inderjeet Singh - * @author Joel Leitch - */ -public class JsonParseException extends RuntimeException { - static final long serialVersionUID = -4086729973971783390L; - - /** - * Creates exception with the specified message. If you are wrapping another exception, consider - * using {@link #JsonParseException(String, Throwable)} instead. - * - * @param msg error message describing a possible cause of this exception. - */ - public JsonParseException(String msg) { - super(msg); - } - - /** - * Creates exception with the specified message and cause. - * - * @param msg error message describing what happened. - * @param cause root exception that caused this exception to be thrown. - */ - public JsonParseException(String msg, Throwable cause) { - super(msg, cause); - } - - /** - * Creates exception with the specified cause. Consider using - * {@link #JsonParseException(String, Throwable)} instead if you can describe what happened. - * - * @param cause root exception that caused this exception to be thrown. - */ - public JsonParseException(Throwable cause) { - super(cause); - } -} diff --git a/src/gson/main/java/com/google/gson/JsonParser.java b/src/gson/main/java/com/google/gson/JsonParser.java deleted file mode 100644 index 4865813..0000000 --- a/src/gson/main/java/com/google/gson/JsonParser.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.gson; - -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; - -import com.google.gson.internal.Streams; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.MalformedJsonException; - -/** - * A parser to parse Json into a parse tree of {@link JsonElement}s - * - * @author Inderjeet Singh - * @author Joel Leitch - * @since 1.3 - */ -public final class JsonParser { - - /** - * Parses the specified JSON string into a parse tree - * - * @param json JSON text - * @return a parse tree of {@link JsonElement}s corresponding to the specified JSON - * @throws JsonParseException if the specified text is not valid JSON - * @since 1.3 - */ - public JsonElement parse(String json) throws JsonSyntaxException { - return parse(new StringReader(json)); - } - - /** - * Parses the specified JSON string into a parse tree - * - * @param json JSON text - * @return a parse tree of {@link JsonElement}s corresponding to the specified JSON - * @throws JsonParseException if the specified text is not valid JSON - * @since 1.3 - */ - public JsonElement parse(Reader json) throws JsonIOException, JsonSyntaxException { - try { - JsonReader jsonReader = new JsonReader(json); - JsonElement element = parse(jsonReader); - if (!element.isJsonNull() && jsonReader.peek() != JsonToken.END_DOCUMENT) { - throw new JsonSyntaxException("Did not consume the entire document."); - } - return element; - } catch (MalformedJsonException e) { - throw new JsonSyntaxException(e); - } catch (IOException e) { - throw new JsonIOException(e); - } catch (NumberFormatException e) { - throw new JsonSyntaxException(e); - } - } - - /** - * Returns the next value from the JSON stream as a parse tree. - * - * @throws JsonParseException if there is an IOException or if the specified - * text is not valid JSON - * @since 1.6 - */ - public JsonElement parse(JsonReader json) throws JsonIOException, JsonSyntaxException { - boolean lenient = json.isLenient(); - json.setLenient(true); - try { - return Streams.parse(json); - } catch (StackOverflowError e) { - throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e); - } catch (OutOfMemoryError e) { - throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e); - } finally { - json.setLenient(lenient); - } - } -} diff --git a/src/gson/main/java/com/google/gson/JsonPrimitive.java b/src/gson/main/java/com/google/gson/JsonPrimitive.java deleted file mode 100644 index adb18fa..0000000 --- a/src/gson/main/java/com/google/gson/JsonPrimitive.java +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -import java.math.BigDecimal; -import java.math.BigInteger; - -import com.google.gson.internal.$Gson$Preconditions; -import com.google.gson.internal.LazilyParsedNumber; - -/** - * A class representing a Json primitive value. A primitive value - * is either a String, a Java primitive, or a Java primitive - * wrapper type. - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -public final class JsonPrimitive extends JsonElement { - - private static final Class[] PRIMITIVE_TYPES = { int.class, long.class, short.class, - float.class, double.class, byte.class, boolean.class, char.class, Integer.class, Long.class, - Short.class, Float.class, Double.class, Byte.class, Boolean.class, Character.class }; - - private Object value; - - /** - * Create a primitive containing a boolean value. - * - * @param bool the value to create the primitive with. - */ - public JsonPrimitive(Boolean bool) { - setValue(bool); - } - - /** - * Create a primitive containing a {@link Number}. - * - * @param number the value to create the primitive with. - */ - public JsonPrimitive(Number number) { - setValue(number); - } - - /** - * Create a primitive containing a String value. - * - * @param string the value to create the primitive with. - */ - public JsonPrimitive(String string) { - setValue(string); - } - - /** - * Create a primitive containing a character. The character is turned into a one character String - * since Json only supports String. - * - * @param c the value to create the primitive with. - */ - public JsonPrimitive(Character c) { - setValue(c); - } - - /** - * Create a primitive using the specified Object. It must be an instance of {@link Number}, a - * Java primitive type, or a String. - * - * @param primitive the value to create the primitive with. - */ - JsonPrimitive(Object primitive) { - setValue(primitive); - } - - /** - * Returns the same value as primitives are immutable. - * @since 2.8.2 - */ - @Override - public JsonPrimitive deepCopy() { - return this; - } - - void setValue(Object primitive) { - if (primitive instanceof Character) { - // convert characters to strings since in JSON, characters are represented as a single - // character string - char c = ((Character) primitive).charValue(); - this.value = String.valueOf(c); - } else { - $Gson$Preconditions.checkArgument(primitive instanceof Number - || isPrimitiveOrString(primitive)); - this.value = primitive; - } - } - - /** - * Check whether this primitive contains a boolean value. - * - * @return true if this primitive contains a boolean value, false otherwise. - */ - public boolean isBoolean() { - return value instanceof Boolean; - } - - /** - * convenience method to get this element as a {@link Boolean}. - * - * @return get this element as a {@link Boolean}. - */ - @Override - Boolean getAsBooleanWrapper() { - return (Boolean) value; - } - - /** - * convenience method to get this element as a boolean value. - * - * @return get this element as a primitive boolean value. - */ - @Override - public boolean getAsBoolean() { - if (isBoolean()) { - return getAsBooleanWrapper().booleanValue(); - } else { - // Check to see if the value as a String is "true" in any case. - return Boolean.parseBoolean(getAsString()); - } - } - - /** - * Check whether this primitive contains a Number. - * - * @return true if this primitive contains a Number, false otherwise. - */ - public boolean isNumber() { - return value instanceof Number; - } - - /** - * convenience method to get this element as a Number. - * - * @return get this element as a Number. - * @throws NumberFormatException if the value contained is not a valid Number. - */ - @Override - public Number getAsNumber() { - return value instanceof String ? new LazilyParsedNumber((String) value) : (Number) value; - } - - /** - * Check whether this primitive contains a String value. - * - * @return true if this primitive contains a String value, false otherwise. - */ - public boolean isString() { - return value instanceof String; - } - - /** - * convenience method to get this element as a String. - * - * @return get this element as a String. - */ - @Override - public String getAsString() { - if (isNumber()) { - return getAsNumber().toString(); - } else if (isBoolean()) { - return getAsBooleanWrapper().toString(); - } else { - return (String) value; - } - } - - /** - * convenience method to get this element as a primitive double. - * - * @return get this element as a primitive double. - * @throws NumberFormatException if the value contained is not a valid double. - */ - @Override - public double getAsDouble() { - return isNumber() ? getAsNumber().doubleValue() : Double.parseDouble(getAsString()); - } - - /** - * convenience method to get this element as a {@link BigDecimal}. - * - * @return get this element as a {@link BigDecimal}. - * @throws NumberFormatException if the value contained is not a valid {@link BigDecimal}. - */ - @Override - public BigDecimal getAsBigDecimal() { - return value instanceof BigDecimal ? (BigDecimal) value : new BigDecimal(value.toString()); - } - - /** - * convenience method to get this element as a {@link BigInteger}. - * - * @return get this element as a {@link BigInteger}. - * @throws NumberFormatException if the value contained is not a valid {@link BigInteger}. - */ - @Override - public BigInteger getAsBigInteger() { - return value instanceof BigInteger ? - (BigInteger) value : new BigInteger(value.toString()); - } - - /** - * convenience method to get this element as a float. - * - * @return get this element as a float. - * @throws NumberFormatException if the value contained is not a valid float. - */ - @Override - public float getAsFloat() { - return isNumber() ? getAsNumber().floatValue() : Float.parseFloat(getAsString()); - } - - /** - * convenience method to get this element as a primitive long. - * - * @return get this element as a primitive long. - * @throws NumberFormatException if the value contained is not a valid long. - */ - @Override - public long getAsLong() { - return isNumber() ? getAsNumber().longValue() : Long.parseLong(getAsString()); - } - - /** - * convenience method to get this element as a primitive short. - * - * @return get this element as a primitive short. - * @throws NumberFormatException if the value contained is not a valid short value. - */ - @Override - public short getAsShort() { - return isNumber() ? getAsNumber().shortValue() : Short.parseShort(getAsString()); - } - - /** - * convenience method to get this element as a primitive integer. - * - * @return get this element as a primitive integer. - * @throws NumberFormatException if the value contained is not a valid integer. - */ - @Override - public int getAsInt() { - return isNumber() ? getAsNumber().intValue() : Integer.parseInt(getAsString()); - } - - @Override - public byte getAsByte() { - return isNumber() ? getAsNumber().byteValue() : Byte.parseByte(getAsString()); - } - - @Override - public char getAsCharacter() { - return getAsString().charAt(0); - } - - private static boolean isPrimitiveOrString(Object target) { - if (target instanceof String) { - return true; - } - - Class classOfPrimitive = target.getClass(); - for (Class standardPrimitive : PRIMITIVE_TYPES) { - if (standardPrimitive.isAssignableFrom(classOfPrimitive)) { - return true; - } - } - return false; - } - - @Override - public int hashCode() { - if (value == null) { - return 31; - } - // Using recommended hashing algorithm from Effective Java for longs and doubles - if (isIntegral(this)) { - long value = getAsNumber().longValue(); - return (int) (value ^ (value >>> 32)); - } - if (value instanceof Number) { - long value = Double.doubleToLongBits(getAsNumber().doubleValue()); - return (int) (value ^ (value >>> 32)); - } - return value.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - JsonPrimitive other = (JsonPrimitive)obj; - if (value == null) { - return other.value == null; - } - if (isIntegral(this) && isIntegral(other)) { - return getAsNumber().longValue() == other.getAsNumber().longValue(); - } - if (value instanceof Number && other.value instanceof Number) { - double a = getAsNumber().doubleValue(); - // Java standard types other than double return true for two NaN. So, need - // special handling for double. - double b = other.getAsNumber().doubleValue(); - return a == b || (Double.isNaN(a) && Double.isNaN(b)); - } - return value.equals(other.value); - } - - /** - * Returns true if the specified number is an integral type - * (Long, Integer, Short, Byte, BigInteger) - */ - private static boolean isIntegral(JsonPrimitive primitive) { - if (primitive.value instanceof Number) { - Number number = (Number) primitive.value; - return number instanceof BigInteger || number instanceof Long || number instanceof Integer - || number instanceof Short || number instanceof Byte; - } - return false; - } -} diff --git a/src/gson/main/java/com/google/gson/JsonSerializationContext.java b/src/gson/main/java/com/google/gson/JsonSerializationContext.java deleted file mode 100644 index ca3ec4f..0000000 --- a/src/gson/main/java/com/google/gson/JsonSerializationContext.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -import java.lang.reflect.Type; - -/** - * Context for serialization that is passed to a custom serializer during invocation of its - * {@link JsonSerializer#serialize(Object, Type, JsonSerializationContext)} method. - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -public interface JsonSerializationContext { - - /** - * Invokes default serialization on the specified object. - * - * @param src the object that needs to be serialized. - * @return a tree of {@link JsonElement}s corresponding to the serialized form of {@code src}. - */ - public JsonElement serialize(Object src); - - /** - * Invokes default serialization on the specified object passing the specific type information. - * It should never be invoked on the element received as a parameter of the - * {@link JsonSerializer#serialize(Object, Type, JsonSerializationContext)} method. Doing - * so will result in an infinite loop since Gson will in-turn call the custom serializer again. - * - * @param src the object that needs to be serialized. - * @param typeOfSrc the actual genericized type of src object. - * @return a tree of {@link JsonElement}s corresponding to the serialized form of {@code src}. - */ - public JsonElement serialize(Object src, Type typeOfSrc); -} diff --git a/src/gson/main/java/com/google/gson/JsonSerializer.java b/src/gson/main/java/com/google/gson/JsonSerializer.java deleted file mode 100644 index a605003..0000000 --- a/src/gson/main/java/com/google/gson/JsonSerializer.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -import java.lang.reflect.Type; - -/** - * Interface representing a custom serializer for Json. You should write a custom serializer, if - * you are not happy with the default serialization done by Gson. You will also need to register - * this serializer through {@link com.google.gson.GsonBuilder#registerTypeAdapter(Type, Object)}. - * - *

Let us look at example where defining a serializer will be useful. The {@code Id} class - * defined below has two fields: {@code clazz} and {@code value}.

- * - *

- * public class Id<T> {
- *   private final Class<T> clazz;
- *   private final long value;
- *
- *   public Id(Class<T> clazz, long value) {
- *     this.clazz = clazz;
- *     this.value = value;
- *   }
- *
- *   public long getValue() {
- *     return value;
- *   }
- * }
- * 

- * - *

The default serialization of {@code Id(com.foo.MyObject.class, 20L)} will be - * {"clazz":com.foo.MyObject,"value":20}. Suppose, you just want the output to be - * the value instead, which is {@code 20} in this case. You can achieve that by writing a custom - * serializer:

- * - *

- * class IdSerializer implements JsonSerializer<Id>() {
- *   public JsonElement serialize(Id id, Type typeOfId, JsonSerializationContext context) {
- *     return new JsonPrimitive(id.getValue());
- *   }
- * }
- * 

- * - *

You will also need to register {@code IdSerializer} with Gson as follows:

- *
- * Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdSerializer()).create();
- * 
- * - *

New applications should prefer {@link TypeAdapter}, whose streaming API - * is more efficient than this interface's tree API. - * - * @author Inderjeet Singh - * @author Joel Leitch - * - * @param type for which the serializer is being registered. It is possible that a serializer - * may be asked to serialize a specific generic type of the T. - */ -public interface JsonSerializer { - - /** - * Gson invokes this call-back method during serialization when it encounters a field of the - * specified type. - * - *

In the implementation of this call-back method, you should consider invoking - * {@link JsonSerializationContext#serialize(Object, Type)} method to create JsonElements for any - * non-trivial field of the {@code src} object. However, you should never invoke it on the - * {@code src} object itself since that will cause an infinite loop (Gson will call your - * call-back method again).

- * - * @param src the object that needs to be converted to Json. - * @param typeOfSrc the actual type (fully genericized version) of the source object. - * @return a JsonElement corresponding to the specified object. - */ - public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context); -} diff --git a/src/gson/main/java/com/google/gson/JsonStreamParser.java b/src/gson/main/java/com/google/gson/JsonStreamParser.java deleted file mode 100644 index f0438db..0000000 --- a/src/gson/main/java/com/google/gson/JsonStreamParser.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.gson; - -import java.io.EOFException; -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.util.Iterator; -import java.util.NoSuchElementException; - -import com.google.gson.internal.Streams; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.MalformedJsonException; - -/** - * A streaming parser that allows reading of multiple {@link JsonElement}s from the specified reader - * asynchronously. - * - *

This class is conditionally thread-safe (see Item 70, Effective Java second edition). To - * properly use this class across multiple threads, you will need to add some external - * synchronization. For example: - * - *

- * JsonStreamParser parser = new JsonStreamParser("['first'] {'second':10} 'third'");
- * JsonElement element;
- * synchronized (parser) {  // synchronize on an object shared by threads
- *   if (parser.hasNext()) {
- *     element = parser.next();
- *   }
- * }
- * 
- * - * @author Inderjeet Singh - * @author Joel Leitch - * @since 1.4 - */ -public final class JsonStreamParser implements Iterator { - private final JsonReader parser; - private final Object lock; - - /** - * @param json The string containing JSON elements concatenated to each other. - * @since 1.4 - */ - public JsonStreamParser(String json) { - this(new StringReader(json)); - } - - /** - * @param reader The data stream containing JSON elements concatenated to each other. - * @since 1.4 - */ - public JsonStreamParser(Reader reader) { - parser = new JsonReader(reader); - parser.setLenient(true); - lock = new Object(); - } - - /** - * Returns the next available {@link JsonElement} on the reader. Null if none available. - * - * @return the next available {@link JsonElement} on the reader. Null if none available. - * @throws JsonParseException if the incoming stream is malformed JSON. - * @since 1.4 - */ - public JsonElement next() throws JsonParseException { - if (!hasNext()) { - throw new NoSuchElementException(); - } - - try { - return Streams.parse(parser); - } catch (StackOverflowError e) { - throw new JsonParseException("Failed parsing JSON source to Json", e); - } catch (OutOfMemoryError e) { - throw new JsonParseException("Failed parsing JSON source to Json", e); - } catch (JsonParseException e) { - throw e.getCause() instanceof EOFException ? new NoSuchElementException() : e; - } - } - - /** - * Returns true if a {@link JsonElement} is available on the input for consumption - * @return true if a {@link JsonElement} is available on the input, false otherwise - * @since 1.4 - */ - public boolean hasNext() { - synchronized (lock) { - try { - return parser.peek() != JsonToken.END_DOCUMENT; - } catch (MalformedJsonException e) { - throw new JsonSyntaxException(e); - } catch (IOException e) { - throw new JsonIOException(e); - } - } - } - - /** - * This optional {@link Iterator} method is not relevant for stream parsing and hence is not - * implemented. - * @since 1.4 - */ - public void remove() { - throw new UnsupportedOperationException(); - } -} diff --git a/src/gson/main/java/com/google/gson/JsonSyntaxException.java b/src/gson/main/java/com/google/gson/JsonSyntaxException.java deleted file mode 100644 index 17c1d3d..0000000 --- a/src/gson/main/java/com/google/gson/JsonSyntaxException.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.gson; - -/** - * This exception is raised when Gson attempts to read (or write) a malformed - * JSON element. - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -public final class JsonSyntaxException extends JsonParseException { - - private static final long serialVersionUID = 1L; - - public JsonSyntaxException(String msg) { - super(msg); - } - - public JsonSyntaxException(String msg, Throwable cause) { - super(msg, cause); - } - - /** - * Creates exception with the specified cause. Consider using - * {@link #JsonSyntaxException(String, Throwable)} instead if you can - * describe what actually happened. - * - * @param cause root exception that caused this exception to be thrown. - */ - public JsonSyntaxException(Throwable cause) { - super(cause); - } -} diff --git a/src/gson/main/java/com/google/gson/LongSerializationPolicy.java b/src/gson/main/java/com/google/gson/LongSerializationPolicy.java deleted file mode 100644 index 7b732ba..0000000 --- a/src/gson/main/java/com/google/gson/LongSerializationPolicy.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -/** - * Defines the expected format for a {@code long} or {@code Long} type when its serialized. - * - * @since 1.3 - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -public enum LongSerializationPolicy { - /** - * This is the "default" serialization policy that will output a {@code long} object as a JSON - * number. For example, assume an object has a long field named "f" then the serialized output - * would be: - * {@code {"f":123}}. - */ - DEFAULT() { - @Override public JsonElement serialize(Long value) { - return new JsonPrimitive(value); - } - }, - - /** - * Serializes a long value as a quoted string. For example, assume an object has a long field - * named "f" then the serialized output would be: - * {@code {"f":"123"}}. - */ - STRING() { - @Override public JsonElement serialize(Long value) { - return new JsonPrimitive(String.valueOf(value)); - } - }; - - /** - * Serialize this {@code value} using this serialization policy. - * - * @param value the long value to be serialized into a {@link JsonElement} - * @return the serialized version of {@code value} - */ - public abstract JsonElement serialize(Long value); -} diff --git a/src/gson/main/java/com/google/gson/TypeAdapter.java b/src/gson/main/java/com/google/gson/TypeAdapter.java deleted file mode 100644 index 4646d27..0000000 --- a/src/gson/main/java/com/google/gson/TypeAdapter.java +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -import com.google.gson.internal.bind.JsonTreeWriter; -import com.google.gson.internal.bind.JsonTreeReader; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.io.StringWriter; -import java.io.Writer; - -/** - * Converts Java objects to and from JSON. - * - *

Defining a type's JSON form

- * By default Gson converts application classes to JSON using its built-in type - * adapters. If Gson's default JSON conversion isn't appropriate for a type, - * extend this class to customize the conversion. Here's an example of a type - * adapter for an (X,Y) coordinate point:
   {@code
- *
- *   public class PointAdapter extends TypeAdapter {
- *     public Point read(JsonReader reader) throws IOException {
- *       if (reader.peek() == JsonToken.NULL) {
- *         reader.nextNull();
- *         return null;
- *       }
- *       String xy = reader.nextString();
- *       String[] parts = xy.split(",");
- *       int x = Integer.parseInt(parts[0]);
- *       int y = Integer.parseInt(parts[1]);
- *       return new Point(x, y);
- *     }
- *     public void write(JsonWriter writer, Point value) throws IOException {
- *       if (value == null) {
- *         writer.nullValue();
- *         return;
- *       }
- *       String xy = value.getX() + "," + value.getY();
- *       writer.value(xy);
- *     }
- *   }}
- * With this type adapter installed, Gson will convert {@code Points} to JSON as - * strings like {@code "5,8"} rather than objects like {@code {"x":5,"y":8}}. In - * this case the type adapter binds a rich Java class to a compact JSON value. - * - *

The {@link #read(JsonReader) read()} method must read exactly one value - * and {@link #write(JsonWriter,Object) write()} must write exactly one value. - * For primitive types this is means readers should make exactly one call to - * {@code nextBoolean()}, {@code nextDouble()}, {@code nextInt()}, {@code - * nextLong()}, {@code nextString()} or {@code nextNull()}. Writers should make - * exactly one call to one of value() or nullValue(). - * For arrays, type adapters should start with a call to {@code beginArray()}, - * convert all elements, and finish with a call to {@code endArray()}. For - * objects, they should start with {@code beginObject()}, convert the object, - * and finish with {@code endObject()}. Failing to convert a value or converting - * too many values may cause the application to crash. - * - *

Type adapters should be prepared to read null from the stream and write it - * to the stream. Alternatively, they should use {@link #nullSafe()} method while - * registering the type adapter with Gson. If your {@code Gson} instance - * has been configured to {@link GsonBuilder#serializeNulls()}, these nulls will be - * written to the final document. Otherwise the value (and the corresponding name - * when writing to a JSON object) will be omitted automatically. In either case - * your type adapter must handle null. - * - *

To use a custom type adapter with Gson, you must register it with a - * {@link GsonBuilder}:

   {@code
- *
- *   GsonBuilder builder = new GsonBuilder();
- *   builder.registerTypeAdapter(Point.class, new PointAdapter());
- *   // if PointAdapter didn't check for nulls in its read/write methods, you should instead use
- *   // builder.registerTypeAdapter(Point.class, new PointAdapter().nullSafe());
- *   ...
- *   Gson gson = builder.create();
- * }
- * - * @since 2.1 - */ -// non-Javadoc: -// -//

JSON Conversion

-//

A type adapter registered with Gson is automatically invoked while serializing -// or deserializing JSON. However, you can also use type adapters directly to serialize -// and deserialize JSON. Here is an example for deserialization:

   {@code
-//
-//   String json = "{'origin':'0,0','points':['1,2','3,4']}";
-//   TypeAdapter graphAdapter = gson.getAdapter(Graph.class);
-//   Graph graph = graphAdapter.fromJson(json);
-// }
-// And an example for serialization:
   {@code
-//
-//   Graph graph = new Graph(...);
-//   TypeAdapter graphAdapter = gson.getAdapter(Graph.class);
-//   String json = graphAdapter.toJson(graph);
-// }
-// -//

Type adapters are type-specific. For example, a {@code -// TypeAdapter} can convert {@code Date} instances to JSON and JSON to -// instances of {@code Date}, but cannot convert any other types. -// -public abstract class TypeAdapter { - - /** - * Writes one JSON value (an array, object, string, number, boolean or null) - * for {@code value}. - * - * @param value the Java object to write. May be null. - */ - public abstract void write(JsonWriter out, T value) throws IOException; - - /** - * Converts {@code value} to a JSON document and writes it to {@code out}. - * Unlike Gson's similar {@link Gson#toJson(JsonElement, Appendable) toJson} - * method, this write is strict. Create a {@link - * JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call - * {@link #write(com.google.gson.stream.JsonWriter, Object)} for lenient - * writing. - * - * @param value the Java object to convert. May be null. - * @since 2.2 - */ - public final void toJson(Writer out, T value) throws IOException { - JsonWriter writer = new JsonWriter(out); - write(writer, value); - } - - /** - * This wrapper method is used to make a type adapter null tolerant. In general, a - * type adapter is required to handle nulls in write and read methods. Here is how this - * is typically done:
- *

   {@code
-   *
-   * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
-   *   new TypeAdapter() {
-   *     public Foo read(JsonReader in) throws IOException {
-   *       if (in.peek() == JsonToken.NULL) {
-   *         in.nextNull();
-   *         return null;
-   *       }
-   *       // read a Foo from in and return it
-   *     }
-   *     public void write(JsonWriter out, Foo src) throws IOException {
-   *       if (src == null) {
-   *         out.nullValue();
-   *         return;
-   *       }
-   *       // write src as JSON to out
-   *     }
-   *   }).create();
-   * }
- * You can avoid this boilerplate handling of nulls by wrapping your type adapter with - * this method. Here is how we will rewrite the above example: - *
   {@code
-   *
-   * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
-   *   new TypeAdapter() {
-   *     public Foo read(JsonReader in) throws IOException {
-   *       // read a Foo from in and return it
-   *     }
-   *     public void write(JsonWriter out, Foo src) throws IOException {
-   *       // write src as JSON to out
-   *     }
-   *   }.nullSafe()).create();
-   * }
- * Note that we didn't need to check for nulls in our type adapter after we used nullSafe. - */ - public final TypeAdapter nullSafe() { - return new TypeAdapter() { - @Override public void write(JsonWriter out, T value) throws IOException { - if (value == null) { - out.nullValue(); - } else { - TypeAdapter.this.write(out, value); - } - } - @Override public T read(JsonReader reader) throws IOException { - if (reader.peek() == JsonToken.NULL) { - reader.nextNull(); - return null; - } - return TypeAdapter.this.read(reader); - } - }; - } - - /** - * Converts {@code value} to a JSON document. Unlike Gson's similar {@link - * Gson#toJson(Object) toJson} method, this write is strict. Create a {@link - * JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call - * {@link #write(com.google.gson.stream.JsonWriter, Object)} for lenient - * writing. - * - * @param value the Java object to convert. May be null. - * @since 2.2 - */ - public final String toJson(T value) { - StringWriter stringWriter = new StringWriter(); - try { - toJson(stringWriter, value); - } catch (IOException e) { - throw new AssertionError(e); // No I/O writing to a StringWriter. - } - return stringWriter.toString(); - } - - /** - * Converts {@code value} to a JSON tree. - * - * @param value the Java object to convert. May be null. - * @return the converted JSON tree. May be {@link JsonNull}. - * @since 2.2 - */ - public final JsonElement toJsonTree(T value) { - try { - JsonTreeWriter jsonWriter = new JsonTreeWriter(); - write(jsonWriter, value); - return jsonWriter.get(); - } catch (IOException e) { - throw new JsonIOException(e); - } - } - - /** - * Reads one JSON value (an array, object, string, number, boolean or null) - * and converts it to a Java object. Returns the converted object. - * - * @return the converted Java object. May be null. - */ - public abstract T read(JsonReader in) throws IOException; - - /** - * Converts the JSON document in {@code in} to a Java object. Unlike Gson's - * similar {@link Gson#fromJson(java.io.Reader, Class) fromJson} method, this - * read is strict. Create a {@link JsonReader#setLenient(boolean) lenient} - * {@code JsonReader} and call {@link #read(JsonReader)} for lenient reading. - * - * @return the converted Java object. May be null. - * @since 2.2 - */ - public final T fromJson(Reader in) throws IOException { - JsonReader reader = new JsonReader(in); - return read(reader); - } - - /** - * Converts the JSON document in {@code json} to a Java object. Unlike Gson's - * similar {@link Gson#fromJson(String, Class) fromJson} method, this read is - * strict. Create a {@link JsonReader#setLenient(boolean) lenient} {@code - * JsonReader} and call {@link #read(JsonReader)} for lenient reading. - * - * @return the converted Java object. May be null. - * @since 2.2 - */ - public final T fromJson(String json) throws IOException { - return fromJson(new StringReader(json)); - } - - /** - * Converts {@code jsonTree} to a Java object. - * - * @param jsonTree the Java object to convert. May be {@link JsonNull}. - * @since 2.2 - */ - public final T fromJsonTree(JsonElement jsonTree) { - try { - JsonReader jsonReader = new JsonTreeReader(jsonTree); - return read(jsonReader); - } catch (IOException e) { - throw new JsonIOException(e); - } - } -} diff --git a/src/gson/main/java/com/google/gson/TypeAdapterFactory.java b/src/gson/main/java/com/google/gson/TypeAdapterFactory.java deleted file mode 100644 index e12a72d..0000000 --- a/src/gson/main/java/com/google/gson/TypeAdapterFactory.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson; - -import com.google.gson.reflect.TypeToken; - -/** - * Creates type adapters for set of related types. Type adapter factories are - * most useful when several types share similar structure in their JSON form. - * - *

Example: Converting enums to lowercase

- * In this example, we implement a factory that creates type adapters for all - * enums. The type adapters will write enums in lowercase, despite the fact - * that they're defined in {@code CONSTANT_CASE} in the corresponding Java - * model:
   {@code
- *
- *   public class LowercaseEnumTypeAdapterFactory implements TypeAdapterFactory {
- *     public  TypeAdapter create(Gson gson, TypeToken type) {
- *       Class rawType = (Class) type.getRawType();
- *       if (!rawType.isEnum()) {
- *         return null;
- *       }
- *
- *       final Map lowercaseToConstant = new HashMap();
- *       for (T constant : rawType.getEnumConstants()) {
- *         lowercaseToConstant.put(toLowercase(constant), constant);
- *       }
- *
- *       return new TypeAdapter() {
- *         public void write(JsonWriter out, T value) throws IOException {
- *           if (value == null) {
- *             out.nullValue();
- *           } else {
- *             out.value(toLowercase(value));
- *           }
- *         }
- *
- *         public T read(JsonReader reader) throws IOException {
- *           if (reader.peek() == JsonToken.NULL) {
- *             reader.nextNull();
- *             return null;
- *           } else {
- *             return lowercaseToConstant.get(reader.nextString());
- *           }
- *         }
- *       };
- *     }
- *
- *     private String toLowercase(Object o) {
- *       return o.toString().toLowerCase(Locale.US);
- *     }
- *   }
- * }
- * - *

Type adapter factories select which types they provide type adapters - * for. If a factory cannot support a given type, it must return null when - * that type is passed to {@link #create}. Factories should expect {@code - * create()} to be called on them for many types and should return null for - * most of those types. In the above example the factory returns null for - * calls to {@code create()} where {@code type} is not an enum. - * - *

A factory is typically called once per type, but the returned type - * adapter may be used many times. It is most efficient to do expensive work - * like reflection in {@code create()} so that the type adapter's {@code - * read()} and {@code write()} methods can be very fast. In this example the - * mapping from lowercase name to enum value is computed eagerly. - * - *

As with type adapters, factories must be registered with a {@link - * com.google.gson.GsonBuilder} for them to take effect:

   {@code
- *
- *  GsonBuilder builder = new GsonBuilder();
- *  builder.registerTypeAdapterFactory(new LowercaseEnumTypeAdapterFactory());
- *  ...
- *  Gson gson = builder.create();
- * }
- * If multiple factories support the same type, the factory registered earlier - * takes precedence. - * - *

Example: composing other type adapters

- * In this example we implement a factory for Guava's {@code Multiset} - * collection type. The factory can be used to create type adapters for - * multisets of any element type: the type adapter for {@code - * Multiset} is different from the type adapter for {@code - * Multiset}. - * - *

The type adapter delegates to another type adapter for the - * multiset elements. It figures out the element type by reflecting on the - * multiset's type token. A {@code Gson} is passed in to {@code create} for - * just this purpose:

   {@code
- *
- *   public class MultisetTypeAdapterFactory implements TypeAdapterFactory {
- *     public  TypeAdapter create(Gson gson, TypeToken typeToken) {
- *       Type type = typeToken.getType();
- *       if (typeToken.getRawType() != Multiset.class
- *           || !(type instanceof ParameterizedType)) {
- *         return null;
- *       }
- *
- *       Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
- *       TypeAdapter elementAdapter = gson.getAdapter(TypeToken.get(elementType));
- *       return (TypeAdapter) newMultisetAdapter(elementAdapter);
- *     }
- *
- *     private  TypeAdapter> newMultisetAdapter(
- *         final TypeAdapter elementAdapter) {
- *       return new TypeAdapter>() {
- *         public void write(JsonWriter out, Multiset value) throws IOException {
- *           if (value == null) {
- *             out.nullValue();
- *             return;
- *           }
- *
- *           out.beginArray();
- *           for (Multiset.Entry entry : value.entrySet()) {
- *             out.value(entry.getCount());
- *             elementAdapter.write(out, entry.getElement());
- *           }
- *           out.endArray();
- *         }
- *
- *         public Multiset read(JsonReader in) throws IOException {
- *           if (in.peek() == JsonToken.NULL) {
- *             in.nextNull();
- *             return null;
- *           }
- *
- *           Multiset result = LinkedHashMultiset.create();
- *           in.beginArray();
- *           while (in.hasNext()) {
- *             int count = in.nextInt();
- *             E element = elementAdapter.read(in);
- *             result.add(element, count);
- *           }
- *           in.endArray();
- *           return result;
- *         }
- *       };
- *     }
- *   }
- * }
- * Delegating from one type adapter to another is extremely powerful; it's - * the foundation of how Gson converts Java objects and collections. Whenever - * possible your factory should retrieve its delegate type adapter in the - * {@code create()} method; this ensures potentially-expensive type adapter - * creation happens only once. - * - * @since 2.1 - */ -public interface TypeAdapterFactory { - - /** - * Returns a type adapter for {@code type}, or null if this factory doesn't - * support {@code type}. - */ - TypeAdapter create(Gson gson, TypeToken type); -} diff --git a/src/gson/main/java/com/google/gson/annotations/Expose.java b/src/gson/main/java/com/google/gson/annotations/Expose.java deleted file mode 100644 index b66dc4f..0000000 --- a/src/gson/main/java/com/google/gson/annotations/Expose.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.annotations; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * An annotation that indicates this member should be exposed for JSON - * serialization or deserialization. - * - *

This annotation has no effect unless you build {@link com.google.gson.Gson} - * with a {@link com.google.gson.GsonBuilder} and invoke - * {@link com.google.gson.GsonBuilder#excludeFieldsWithoutExposeAnnotation()} - * method.

- * - *

Here is an example of how this annotation is meant to be used: - *

- * public class User {
- *   @Expose private String firstName;
- *   @Expose(serialize = false) private String lastName;
- *   @Expose (serialize = false, deserialize = false) private String emailAddress;
- *   private String password;
- * }
- * 

- * If you created Gson with {@code new Gson()}, the {@code toJson()} and {@code fromJson()} - * methods will use the {@code password} field along-with {@code firstName}, {@code lastName}, - * and {@code emailAddress} for serialization and deserialization. However, if you created Gson - * with {@code Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()} - * then the {@code toJson()} and {@code fromJson()} methods of Gson will exclude the - * {@code password} field. This is because the {@code password} field is not marked with the - * {@code @Expose} annotation. Gson will also exclude {@code lastName} and {@code emailAddress} - * from serialization since {@code serialize} is set to {@code false}. Similarly, Gson will - * exclude {@code emailAddress} from deserialization since {@code deserialize} is set to false. - * - *

Note that another way to achieve the same effect would have been to just mark the - * {@code password} field as {@code transient}, and Gson would have excluded it even with default - * settings. The {@code @Expose} annotation is useful in a style of programming where you want to - * explicitly specify all fields that should get considered for serialization or deserialization. - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.FIELD) -public @interface Expose { - - /** - * If {@code true}, the field marked with this annotation is written out in the JSON while - * serializing. If {@code false}, the field marked with this annotation is skipped from the - * serialized output. Defaults to {@code true}. - * @since 1.4 - */ - public boolean serialize() default true; - - /** - * If {@code true}, the field marked with this annotation is deserialized from the JSON. - * If {@code false}, the field marked with this annotation is skipped during deserialization. - * Defaults to {@code true}. - * @since 1.4 - */ - public boolean deserialize() default true; -} diff --git a/src/gson/main/java/com/google/gson/annotations/JsonAdapter.java b/src/gson/main/java/com/google/gson/annotations/JsonAdapter.java deleted file mode 100644 index 93163f8..0000000 --- a/src/gson/main/java/com/google/gson/annotations/JsonAdapter.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2014 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.annotations; - -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonSerializer; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * An annotation that indicates the Gson {@link TypeAdapter} to use with a class - * or field. - * - *

Here is an example of how this annotation is used:

- *
- * @JsonAdapter(UserJsonAdapter.class)
- * public class User {
- *   public final String firstName, lastName;
- *   private User(String firstName, String lastName) {
- *     this.firstName = firstName;
- *     this.lastName = lastName;
- *   }
- * }
- * public class UserJsonAdapter extends TypeAdapter<User> {
- *   @Override public void write(JsonWriter out, User user) throws IOException {
- *     // implement write: combine firstName and lastName into name
- *     out.beginObject();
- *     out.name("name");
- *     out.value(user.firstName + " " + user.lastName);
- *     out.endObject();
- *     // implement the write method
- *   }
- *   @Override public User read(JsonReader in) throws IOException {
- *     // implement read: split name into firstName and lastName
- *     in.beginObject();
- *     in.nextName();
- *     String[] nameParts = in.nextString().split(" ");
- *     in.endObject();
- *     return new User(nameParts[0], nameParts[1]);
- *   }
- * }
- * 
- * - * Since User class specified UserJsonAdapter.class in @JsonAdapter annotation, it - * will automatically be invoked to serialize/deserialize User instances.
- * - *

Here is an example of how to apply this annotation to a field. - *

- * private static final class Gadget {
- *   @JsonAdapter(UserJsonAdapter2.class)
- *   final User user;
- *   Gadget(User user) {
- *     this.user = user;
- *   }
- * }
- * 
- * - * It's possible to specify different type adapters on a field, that - * field's type, and in the {@link com.google.gson.GsonBuilder}. Field - * annotations take precedence over {@code GsonBuilder}-registered type - * adapters, which in turn take precedence over annotated types. - * - *

The class referenced by this annotation must be either a {@link - * TypeAdapter} or a {@link TypeAdapterFactory}, or must implement one - * or both of {@link JsonDeserializer} or {@link JsonSerializer}. - * Using {@link TypeAdapterFactory} makes it possible to delegate - * to the enclosing {@code Gson} instance. - * - * @since 2.3 - * - * @author Inderjeet Singh - * @author Joel Leitch - * @author Jesse Wilson - */ -// Note that the above example is taken from AdaptAnnotationTest. -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE, ElementType.FIELD}) -public @interface JsonAdapter { - - /** Either a {@link TypeAdapter} or {@link TypeAdapterFactory}, or one or both of {@link JsonDeserializer} or {@link JsonSerializer}. */ - Class value(); - - /** false, to be able to handle {@code null} values within the adapter, default value is true. */ - boolean nullSafe() default true; - -} diff --git a/src/gson/main/java/com/google/gson/annotations/SerializedName.java b/src/gson/main/java/com/google/gson/annotations/SerializedName.java deleted file mode 100644 index 9e8e4b8..0000000 --- a/src/gson/main/java/com/google/gson/annotations/SerializedName.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.annotations; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * An annotation that indicates this member should be serialized to JSON with - * the provided name value as its field name. - * - *

This annotation will override any {@link com.google.gson.FieldNamingPolicy}, including - * the default field naming policy, that may have been set on the {@link com.google.gson.Gson} - * instance. A different naming policy can set using the {@code GsonBuilder} class. See - * {@link com.google.gson.GsonBuilder#setFieldNamingPolicy(com.google.gson.FieldNamingPolicy)} - * for more information.

- * - *

Here is an example of how this annotation is meant to be used:

- *
- * public class MyClass {
- *   @SerializedName("name") String a;
- *   @SerializedName(value="name1", alternate={"name2", "name3"}) String b;
- *   String c;
- *
- *   public MyClass(String a, String b, String c) {
- *     this.a = a;
- *     this.b = b;
- *     this.c = c;
- *   }
- * }
- * 
- * - *

The following shows the output that is generated when serializing an instance of the - * above example class:

- *
- * MyClass target = new MyClass("v1", "v2", "v3");
- * Gson gson = new Gson();
- * String json = gson.toJson(target);
- * System.out.println(json);
- *
- * ===== OUTPUT =====
- * {"name":"v1","name1":"v2","c":"v3"}
- * 
- * - *

NOTE: The value you specify in this annotation must be a valid JSON field name.

- * While deserializing, all values specified in the annotation will be deserialized into the field. - * For example: - *
- *   MyClass target = gson.fromJson("{'name1':'v1'}", MyClass.class);
- *   assertEquals("v1", target.b);
- *   target = gson.fromJson("{'name2':'v2'}", MyClass.class);
- *   assertEquals("v2", target.b);
- *   target = gson.fromJson("{'name3':'v3'}", MyClass.class);
- *   assertEquals("v3", target.b);
- * 
- * Note that MyClass.b is now deserialized from either name1, name2 or name3. - * - * @see com.google.gson.FieldNamingPolicy - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.METHOD}) -public @interface SerializedName { - - /** - * @return the desired name of the field when it is serialized or deserialized - */ - String value(); - /** - * @return the alternative names of the field when it is deserialized - */ - String[] alternate() default {}; -} diff --git a/src/gson/main/java/com/google/gson/annotations/Since.java b/src/gson/main/java/com/google/gson/annotations/Since.java deleted file mode 100644 index c6b2182..0000000 --- a/src/gson/main/java/com/google/gson/annotations/Since.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.annotations; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * An annotation that indicates the version number since a member or a type has been present. - * This annotation is useful to manage versioning of your Json classes for a web-service. - * - *

- * This annotation has no effect unless you build {@link com.google.gson.Gson} with a - * {@link com.google.gson.GsonBuilder} and invoke - * {@link com.google.gson.GsonBuilder#setVersion(double)} method. - * - *

Here is an example of how this annotation is meant to be used:

- *
- * public class User {
- *   private String firstName;
- *   private String lastName;
- *   @Since(1.0) private String emailAddress;
- *   @Since(1.0) private String password;
- *   @Since(1.1) private Address address;
- * }
- * 
- * - *

If you created Gson with {@code new Gson()}, the {@code toJson()} and {@code fromJson()} - * methods will use all the fields for serialization and deserialization. However, if you created - * Gson with {@code Gson gson = new GsonBuilder().setVersion(1.0).create()} then the - * {@code toJson()} and {@code fromJson()} methods of Gson will exclude the {@code address} field - * since it's version number is set to {@code 1.1}.

- * - * @author Inderjeet Singh - * @author Joel Leitch - */ -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.TYPE}) -public @interface Since { - /** - * the value indicating a version number since this member - * or type has been present. - */ - double value(); -} diff --git a/src/gson/main/java/com/google/gson/annotations/Until.java b/src/gson/main/java/com/google/gson/annotations/Until.java deleted file mode 100644 index 676a4b9..0000000 --- a/src/gson/main/java/com/google/gson/annotations/Until.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.annotations; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * An annotation that indicates the version number until a member or a type should be present. - * Basically, if Gson is created with a version number that exceeds the value stored in the - * {@code Until} annotation then the field will be ignored from the JSON output. This annotation - * is useful to manage versioning of your JSON classes for a web-service. - * - *

- * This annotation has no effect unless you build {@link com.google.gson.Gson} with a - * {@link com.google.gson.GsonBuilder} and invoke - * {@link com.google.gson.GsonBuilder#setVersion(double)} method. - * - *

Here is an example of how this annotation is meant to be used:

- *
- * public class User {
- *   private String firstName;
- *   private String lastName;
- *   @Until(1.1) private String emailAddress;
- *   @Until(1.1) private String password;
- * }
- * 
- * - *

If you created Gson with {@code new Gson()}, the {@code toJson()} and {@code fromJson()} - * methods will use all the fields for serialization and deserialization. However, if you created - * Gson with {@code Gson gson = new GsonBuilder().setVersion(1.2).create()} then the - * {@code toJson()} and {@code fromJson()} methods of Gson will exclude the {@code emailAddress} - * and {@code password} fields from the example above, because the version number passed to the - * GsonBuilder, {@code 1.2}, exceeds the version number set on the {@code Until} annotation, - * {@code 1.1}, for those fields. - * - * @author Inderjeet Singh - * @author Joel Leitch - * @since 1.3 - */ -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.TYPE}) -public @interface Until { - - /** - * the value indicating a version number until this member - * or type should be ignored. - */ - double value(); -} diff --git a/src/gson/main/java/com/google/gson/annotations/package-info.java b/src/gson/main/java/com/google/gson/annotations/package-info.java deleted file mode 100644 index 1c461fd..0000000 --- a/src/gson/main/java/com/google/gson/annotations/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * This package provides annotations that can be used with {@link com.google.gson.Gson}. - * - * @author Inderjeet Singh, Joel Leitch - */ -package com.google.gson.annotations; \ No newline at end of file diff --git a/src/gson/main/java/com/google/gson/internal/$Gson$Preconditions.java b/src/gson/main/java/com/google/gson/internal/$Gson$Preconditions.java deleted file mode 100644 index 83e5730..0000000 --- a/src/gson/main/java/com/google/gson/internal/$Gson$Preconditions.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal; - -/** - * A simple utility class used to check method Preconditions. - * - *

- * public long divideBy(long value) {
- *   Preconditions.checkArgument(value != 0);
- *   return this.value / value;
- * }
- * 
- * - * @author Inderjeet Singh - * @author Joel Leitch - */ -public final class $Gson$Preconditions { - private $Gson$Preconditions() { - throw new UnsupportedOperationException(); - } - - public static T checkNotNull(T obj) { - if (obj == null) { - throw new NullPointerException(); - } - return obj; - } - - public static void checkArgument(boolean condition) { - if (!condition) { - throw new IllegalArgumentException(); - } - } -} diff --git a/src/gson/main/java/com/google/gson/internal/$Gson$Types.java b/src/gson/main/java/com/google/gson/internal/$Gson$Types.java deleted file mode 100644 index 6739453..0000000 --- a/src/gson/main/java/com/google/gson/internal/$Gson$Types.java +++ /dev/null @@ -1,599 +0,0 @@ -/** - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal; - -import java.io.Serializable; -import java.lang.reflect.Array; -import java.lang.reflect.GenericArrayType; -import java.lang.reflect.GenericDeclaration; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.lang.reflect.WildcardType; -import java.util.Arrays; -import java.util.Collection; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Properties; - -import static com.google.gson.internal.$Gson$Preconditions.checkArgument; -import static com.google.gson.internal.$Gson$Preconditions.checkNotNull; - -/** - * Static methods for working with types. - * - * @author Bob Lee - * @author Jesse Wilson - */ -public final class $Gson$Types { - static final Type[] EMPTY_TYPE_ARRAY = new Type[] {}; - - private $Gson$Types() { - throw new UnsupportedOperationException(); - } - - /** - * Returns a new parameterized type, applying {@code typeArguments} to - * {@code rawType} and enclosed by {@code ownerType}. - * - * @return a {@link java.io.Serializable serializable} parameterized type. - */ - public static ParameterizedType newParameterizedTypeWithOwner( - Type ownerType, Type rawType, Type... typeArguments) { - return new ParameterizedTypeImpl(ownerType, rawType, typeArguments); - } - - /** - * Returns an array type whose elements are all instances of - * {@code componentType}. - * - * @return a {@link java.io.Serializable serializable} generic array type. - */ - public static GenericArrayType arrayOf(Type componentType) { - return new GenericArrayTypeImpl(componentType); - } - - /** - * Returns a type that represents an unknown type that extends {@code bound}. - * For example, if {@code bound} is {@code CharSequence.class}, this returns - * {@code ? extends CharSequence}. If {@code bound} is {@code Object.class}, - * this returns {@code ?}, which is shorthand for {@code ? extends Object}. - */ - public static WildcardType subtypeOf(Type bound) { - Type[] upperBounds; - if (bound instanceof WildcardType) { - upperBounds = ((WildcardType) bound).getUpperBounds(); - } else { - upperBounds = new Type[] { bound }; - } - return new WildcardTypeImpl(upperBounds, EMPTY_TYPE_ARRAY); - } - - /** - * Returns a type that represents an unknown supertype of {@code bound}. For - * example, if {@code bound} is {@code String.class}, this returns {@code ? - * super String}. - */ - public static WildcardType supertypeOf(Type bound) { - Type[] lowerBounds; - if (bound instanceof WildcardType) { - lowerBounds = ((WildcardType) bound).getLowerBounds(); - } else { - lowerBounds = new Type[] { bound }; - } - return new WildcardTypeImpl(new Type[] { Object.class }, lowerBounds); - } - - /** - * Returns a type that is functionally equal but not necessarily equal - * according to {@link Object#equals(Object) Object.equals()}. The returned - * type is {@link java.io.Serializable}. - */ - public static Type canonicalize(Type type) { - if (type instanceof Class) { - Class c = (Class) type; - return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c; - - } else if (type instanceof ParameterizedType) { - ParameterizedType p = (ParameterizedType) type; - return new ParameterizedTypeImpl(p.getOwnerType(), - p.getRawType(), p.getActualTypeArguments()); - - } else if (type instanceof GenericArrayType) { - GenericArrayType g = (GenericArrayType) type; - return new GenericArrayTypeImpl(g.getGenericComponentType()); - - } else if (type instanceof WildcardType) { - WildcardType w = (WildcardType) type; - return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds()); - - } else { - // type is either serializable as-is or unsupported - return type; - } - } - - public static Class getRawType(Type type) { - if (type instanceof Class) { - // type is a normal class. - return (Class) type; - - } else if (type instanceof ParameterizedType) { - ParameterizedType parameterizedType = (ParameterizedType) type; - - // I'm not exactly sure why getRawType() returns Type instead of Class. - // Neal isn't either but suspects some pathological case related - // to nested classes exists. - Type rawType = parameterizedType.getRawType(); - checkArgument(rawType instanceof Class); - return (Class) rawType; - - } else if (type instanceof GenericArrayType) { - Type componentType = ((GenericArrayType)type).getGenericComponentType(); - return Array.newInstance(getRawType(componentType), 0).getClass(); - - } else if (type instanceof TypeVariable) { - // we could use the variable's bounds, but that won't work if there are multiple. - // having a raw type that's more general than necessary is okay - return Object.class; - - } else if (type instanceof WildcardType) { - return getRawType(((WildcardType) type).getUpperBounds()[0]); - - } else { - String className = type == null ? "null" : type.getClass().getName(); - throw new IllegalArgumentException("Expected a Class, ParameterizedType, or " - + "GenericArrayType, but <" + type + "> is of type " + className); - } - } - - static boolean equal(Object a, Object b) { - return a == b || (a != null && a.equals(b)); - } - - /** - * Returns true if {@code a} and {@code b} are equal. - */ - public static boolean equals(Type a, Type b) { - if (a == b) { - // also handles (a == null && b == null) - return true; - - } else if (a instanceof Class) { - // Class already specifies equals(). - return a.equals(b); - - } else if (a instanceof ParameterizedType) { - if (!(b instanceof ParameterizedType)) { - return false; - } - - // TODO: save a .clone() call - ParameterizedType pa = (ParameterizedType) a; - ParameterizedType pb = (ParameterizedType) b; - return equal(pa.getOwnerType(), pb.getOwnerType()) - && pa.getRawType().equals(pb.getRawType()) - && Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments()); - - } else if (a instanceof GenericArrayType) { - if (!(b instanceof GenericArrayType)) { - return false; - } - - GenericArrayType ga = (GenericArrayType) a; - GenericArrayType gb = (GenericArrayType) b; - return equals(ga.getGenericComponentType(), gb.getGenericComponentType()); - - } else if (a instanceof WildcardType) { - if (!(b instanceof WildcardType)) { - return false; - } - - WildcardType wa = (WildcardType) a; - WildcardType wb = (WildcardType) b; - return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds()) - && Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds()); - - } else if (a instanceof TypeVariable) { - if (!(b instanceof TypeVariable)) { - return false; - } - TypeVariable va = (TypeVariable) a; - TypeVariable vb = (TypeVariable) b; - return va.getGenericDeclaration() == vb.getGenericDeclaration() - && va.getName().equals(vb.getName()); - - } else { - // This isn't a type we support. Could be a generic array type, wildcard type, etc. - return false; - } - } - - static int hashCodeOrZero(Object o) { - return o != null ? o.hashCode() : 0; - } - - public static String typeToString(Type type) { - return type instanceof Class ? ((Class) type).getName() : type.toString(); - } - - /** - * Returns the generic supertype for {@code supertype}. For example, given a class {@code - * IntegerSet}, the result for when supertype is {@code Set.class} is {@code Set} and the - * result when the supertype is {@code Collection.class} is {@code Collection}. - */ - static Type getGenericSupertype(Type context, Class rawType, Class toResolve) { - if (toResolve == rawType) { - return context; - } - - // we skip searching through interfaces if unknown is an interface - if (toResolve.isInterface()) { - Class[] interfaces = rawType.getInterfaces(); - for (int i = 0, length = interfaces.length; i < length; i++) { - if (interfaces[i] == toResolve) { - return rawType.getGenericInterfaces()[i]; - } else if (toResolve.isAssignableFrom(interfaces[i])) { - return getGenericSupertype(rawType.getGenericInterfaces()[i], interfaces[i], toResolve); - } - } - } - - // check our supertypes - if (!rawType.isInterface()) { - while (rawType != Object.class) { - Class rawSupertype = rawType.getSuperclass(); - if (rawSupertype == toResolve) { - return rawType.getGenericSuperclass(); - } else if (toResolve.isAssignableFrom(rawSupertype)) { - return getGenericSupertype(rawType.getGenericSuperclass(), rawSupertype, toResolve); - } - rawType = rawSupertype; - } - } - - // we can't resolve this further - return toResolve; - } - - /** - * Returns the generic form of {@code supertype}. For example, if this is {@code - * ArrayList}, this returns {@code Iterable} given the input {@code - * Iterable.class}. - * - * @param supertype a superclass of, or interface implemented by, this. - */ - static Type getSupertype(Type context, Class contextRawType, Class supertype) { - checkArgument(supertype.isAssignableFrom(contextRawType)); - return resolve(context, contextRawType, - $Gson$Types.getGenericSupertype(context, contextRawType, supertype)); - } - - /** - * Returns the component type of this array type. - * @throws ClassCastException if this type is not an array. - */ - public static Type getArrayComponentType(Type array) { - return array instanceof GenericArrayType - ? ((GenericArrayType) array).getGenericComponentType() - : ((Class) array).getComponentType(); - } - - /** - * Returns the element type of this collection type. - * @throws IllegalArgumentException if this type is not a collection. - */ - public static Type getCollectionElementType(Type context, Class contextRawType) { - Type collectionType = getSupertype(context, contextRawType, Collection.class); - - if (collectionType instanceof WildcardType) { - collectionType = ((WildcardType)collectionType).getUpperBounds()[0]; - } - if (collectionType instanceof ParameterizedType) { - return ((ParameterizedType) collectionType).getActualTypeArguments()[0]; - } - return Object.class; - } - - /** - * Returns a two element array containing this map's key and value types in - * positions 0 and 1 respectively. - */ - public static Type[] getMapKeyAndValueTypes(Type context, Class contextRawType) { - /* - * Work around a problem with the declaration of java.util.Properties. That - * class should extend Hashtable, but it's declared to - * extend Hashtable. - */ - if (context == Properties.class) { - return new Type[] { String.class, String.class }; // TODO: test subclasses of Properties! - } - - Type mapType = getSupertype(context, contextRawType, Map.class); - // TODO: strip wildcards? - if (mapType instanceof ParameterizedType) { - ParameterizedType mapParameterizedType = (ParameterizedType) mapType; - return mapParameterizedType.getActualTypeArguments(); - } - return new Type[] { Object.class, Object.class }; - } - - public static Type resolve(Type context, Class contextRawType, Type toResolve) { - // this implementation is made a little more complicated in an attempt to avoid object-creation - while (true) { - if (toResolve instanceof TypeVariable) { - TypeVariable typeVariable = (TypeVariable) toResolve; - toResolve = resolveTypeVariable(context, contextRawType, typeVariable); - if (toResolve == typeVariable) { - return toResolve; - } - - } else if (toResolve instanceof Class && ((Class) toResolve).isArray()) { - Class original = (Class) toResolve; - Type componentType = original.getComponentType(); - Type newComponentType = resolve(context, contextRawType, componentType); - return componentType == newComponentType - ? original - : arrayOf(newComponentType); - - } else if (toResolve instanceof GenericArrayType) { - GenericArrayType original = (GenericArrayType) toResolve; - Type componentType = original.getGenericComponentType(); - Type newComponentType = resolve(context, contextRawType, componentType); - return componentType == newComponentType - ? original - : arrayOf(newComponentType); - - } else if (toResolve instanceof ParameterizedType) { - ParameterizedType original = (ParameterizedType) toResolve; - Type ownerType = original.getOwnerType(); - Type newOwnerType = resolve(context, contextRawType, ownerType); - boolean changed = newOwnerType != ownerType; - - Type[] args = original.getActualTypeArguments(); - for (int t = 0, length = args.length; t < length; t++) { - Type resolvedTypeArgument = resolve(context, contextRawType, args[t]); - if (resolvedTypeArgument != args[t]) { - if (!changed) { - args = args.clone(); - changed = true; - } - args[t] = resolvedTypeArgument; - } - } - - return changed - ? newParameterizedTypeWithOwner(newOwnerType, original.getRawType(), args) - : original; - - } else if (toResolve instanceof WildcardType) { - WildcardType original = (WildcardType) toResolve; - Type[] originalLowerBound = original.getLowerBounds(); - Type[] originalUpperBound = original.getUpperBounds(); - - if (originalLowerBound.length == 1) { - Type lowerBound = resolve(context, contextRawType, originalLowerBound[0]); - if (lowerBound != originalLowerBound[0]) { - return supertypeOf(lowerBound); - } - } else if (originalUpperBound.length == 1) { - Type upperBound = resolve(context, contextRawType, originalUpperBound[0]); - if (upperBound != originalUpperBound[0]) { - return subtypeOf(upperBound); - } - } - return original; - - } else { - return toResolve; - } - } - } - - static Type resolveTypeVariable(Type context, Class contextRawType, TypeVariable unknown) { - Class declaredByRaw = declaringClassOf(unknown); - - // we can't reduce this further - if (declaredByRaw == null) { - return unknown; - } - - Type declaredBy = getGenericSupertype(context, contextRawType, declaredByRaw); - if (declaredBy instanceof ParameterizedType) { - int index = indexOf(declaredByRaw.getTypeParameters(), unknown); - return ((ParameterizedType) declaredBy).getActualTypeArguments()[index]; - } - - return unknown; - } - - private static int indexOf(Object[] array, Object toFind) { - for (int i = 0, length = array.length; i < length; i++) { - if (toFind.equals(array[i])) { - return i; - } - } - throw new NoSuchElementException(); - } - - /** - * Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by - * a class. - */ - private static Class declaringClassOf(TypeVariable typeVariable) { - GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration(); - return genericDeclaration instanceof Class - ? (Class) genericDeclaration - : null; - } - - static void checkNotPrimitive(Type type) { - checkArgument(!(type instanceof Class) || !((Class) type).isPrimitive()); - } - - private static final class ParameterizedTypeImpl implements ParameterizedType, Serializable { - private final Type ownerType; - private final Type rawType; - private final Type[] typeArguments; - - public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) { - // require an owner type if the raw type needs it - if (rawType instanceof Class) { - Class rawTypeAsClass = (Class) rawType; - boolean isStaticOrTopLevelClass = Modifier.isStatic(rawTypeAsClass.getModifiers()) - || rawTypeAsClass.getEnclosingClass() == null; - checkArgument(ownerType != null || isStaticOrTopLevelClass); - } - - this.ownerType = ownerType == null ? null : canonicalize(ownerType); - this.rawType = canonicalize(rawType); - this.typeArguments = typeArguments.clone(); - for (int t = 0, length = this.typeArguments.length; t < length; t++) { - checkNotNull(this.typeArguments[t]); - checkNotPrimitive(this.typeArguments[t]); - this.typeArguments[t] = canonicalize(this.typeArguments[t]); - } - } - - public Type[] getActualTypeArguments() { - return typeArguments.clone(); - } - - public Type getRawType() { - return rawType; - } - - public Type getOwnerType() { - return ownerType; - } - - @Override public boolean equals(Object other) { - return other instanceof ParameterizedType - && $Gson$Types.equals(this, (ParameterizedType) other); - } - - @Override public int hashCode() { - return Arrays.hashCode(typeArguments) - ^ rawType.hashCode() - ^ hashCodeOrZero(ownerType); - } - - @Override public String toString() { - int length = typeArguments.length; - if (length == 0) { - return typeToString(rawType); - } - - StringBuilder stringBuilder = new StringBuilder(30 * (length + 1)); - stringBuilder.append(typeToString(rawType)).append("<").append(typeToString(typeArguments[0])); - for (int i = 1; i < length; i++) { - stringBuilder.append(", ").append(typeToString(typeArguments[i])); - } - return stringBuilder.append(">").toString(); - } - - private static final long serialVersionUID = 0; - } - - private static final class GenericArrayTypeImpl implements GenericArrayType, Serializable { - private final Type componentType; - - public GenericArrayTypeImpl(Type componentType) { - this.componentType = canonicalize(componentType); - } - - public Type getGenericComponentType() { - return componentType; - } - - @Override public boolean equals(Object o) { - return o instanceof GenericArrayType - && $Gson$Types.equals(this, (GenericArrayType) o); - } - - @Override public int hashCode() { - return componentType.hashCode(); - } - - @Override public String toString() { - return typeToString(componentType) + "[]"; - } - - private static final long serialVersionUID = 0; - } - - /** - * The WildcardType interface supports multiple upper bounds and multiple - * lower bounds. We only support what the Java 6 language needs - at most one - * bound. If a lower bound is set, the upper bound must be Object.class. - */ - private static final class WildcardTypeImpl implements WildcardType, Serializable { - private final Type upperBound; - private final Type lowerBound; - - public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) { - checkArgument(lowerBounds.length <= 1); - checkArgument(upperBounds.length == 1); - - if (lowerBounds.length == 1) { - checkNotNull(lowerBounds[0]); - checkNotPrimitive(lowerBounds[0]); - checkArgument(upperBounds[0] == Object.class); - this.lowerBound = canonicalize(lowerBounds[0]); - this.upperBound = Object.class; - - } else { - checkNotNull(upperBounds[0]); - checkNotPrimitive(upperBounds[0]); - this.lowerBound = null; - this.upperBound = canonicalize(upperBounds[0]); - } - } - - public Type[] getUpperBounds() { - return new Type[] { upperBound }; - } - - public Type[] getLowerBounds() { - return lowerBound != null ? new Type[] { lowerBound } : EMPTY_TYPE_ARRAY; - } - - @Override public boolean equals(Object other) { - return other instanceof WildcardType - && $Gson$Types.equals(this, (WildcardType) other); - } - - @Override public int hashCode() { - // this equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds()); - return (lowerBound != null ? 31 + lowerBound.hashCode() : 1) - ^ (31 + upperBound.hashCode()); - } - - @Override public String toString() { - if (lowerBound != null) { - return "? super " + typeToString(lowerBound); - } else if (upperBound == Object.class) { - return "?"; - } else { - return "? extends " + typeToString(upperBound); - } - } - - private static final long serialVersionUID = 0; - } -} diff --git a/src/gson/main/java/com/google/gson/internal/ConstructorConstructor.java b/src/gson/main/java/com/google/gson/internal/ConstructorConstructor.java deleted file mode 100644 index ffe3cb4..0000000 --- a/src/gson/main/java/com/google/gson/internal/ConstructorConstructor.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collection; -import java.util.EnumSet; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Queue; -import java.util.Set; -import java.util.SortedMap; -import java.util.SortedSet; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.ConcurrentNavigableMap; -import java.util.concurrent.ConcurrentSkipListMap; - -import com.google.gson.InstanceCreator; -import com.google.gson.JsonIOException; -import com.google.gson.reflect.TypeToken; - -/** - * Returns a function that can construct an instance of a requested type. - */ -public final class ConstructorConstructor { - private final Map> instanceCreators; - - public ConstructorConstructor(Map> instanceCreators) { - this.instanceCreators = instanceCreators; - } - - public ObjectConstructor get(TypeToken typeToken) { - final Type type = typeToken.getType(); - final Class rawType = typeToken.getRawType(); - - // first try an instance creator - - @SuppressWarnings("unchecked") // types must agree - final InstanceCreator typeCreator = (InstanceCreator) instanceCreators.get(type); - if (typeCreator != null) { - return new ObjectConstructor() { - @Override public T construct() { - return typeCreator.createInstance(type); - } - }; - } - - // Next try raw type match for instance creators - @SuppressWarnings("unchecked") // types must agree - final InstanceCreator rawTypeCreator = - (InstanceCreator) instanceCreators.get(rawType); - if (rawTypeCreator != null) { - return new ObjectConstructor() { - @Override public T construct() { - return rawTypeCreator.createInstance(type); - } - }; - } - - ObjectConstructor defaultConstructor = newDefaultConstructor(rawType); - if (defaultConstructor != null) { - return defaultConstructor; - } - - ObjectConstructor defaultImplementation = newDefaultImplementationConstructor(type, rawType); - if (defaultImplementation != null) { - return defaultImplementation; - } - - // finally try unsafe - return newUnsafeAllocator(type, rawType); - } - - private ObjectConstructor newDefaultConstructor(Class rawType) { - try { - final Constructor constructor = rawType.getDeclaredConstructor(); - if (!constructor.isAccessible()) { - constructor.setAccessible(true); - } - return new ObjectConstructor() { - @SuppressWarnings("unchecked") // T is the same raw type as is requested - @Override public T construct() { - try { - Object[] args = null; - return (T) constructor.newInstance(args); - } catch (InstantiationException e) { - // TODO: JsonParseException ? - throw new RuntimeException("Failed to invoke " + constructor + " with no args", e); - } catch (InvocationTargetException e) { - // TODO: don't wrap if cause is unchecked! - // TODO: JsonParseException ? - throw new RuntimeException("Failed to invoke " + constructor + " with no args", - e.getTargetException()); - } catch (IllegalAccessException e) { - throw new AssertionError(e); - } - } - }; - } catch (NoSuchMethodException e) { - return null; - } - } - - /** - * Constructors for common interface types like Map and List and their - * subtypes. - */ - @SuppressWarnings("unchecked") // use runtime checks to guarantee that 'T' is what it is - private ObjectConstructor newDefaultImplementationConstructor( - final Type type, Class rawType) { - if (Collection.class.isAssignableFrom(rawType)) { - if (SortedSet.class.isAssignableFrom(rawType)) { - return new ObjectConstructor() { - @Override public T construct() { - return (T) new TreeSet(); - } - }; - } else if (EnumSet.class.isAssignableFrom(rawType)) { - return new ObjectConstructor() { - @SuppressWarnings("rawtypes") - @Override public T construct() { - if (type instanceof ParameterizedType) { - Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0]; - if (elementType instanceof Class) { - return (T) EnumSet.noneOf((Class)elementType); - } else { - throw new JsonIOException("Invalid EnumSet type: " + type.toString()); - } - } else { - throw new JsonIOException("Invalid EnumSet type: " + type.toString()); - } - } - }; - } else if (Set.class.isAssignableFrom(rawType)) { - return new ObjectConstructor() { - @Override public T construct() { - return (T) new LinkedHashSet(); - } - }; - } else if (Queue.class.isAssignableFrom(rawType)) { - return new ObjectConstructor() { - @Override public T construct() { - return (T) new ArrayDeque(); - } - }; - } else { - return new ObjectConstructor() { - @Override public T construct() { - return (T) new ArrayList(); - } - }; - } - } - - if (Map.class.isAssignableFrom(rawType)) { - if (ConcurrentNavigableMap.class.isAssignableFrom(rawType)) { - return new ObjectConstructor() { - @Override public T construct() { - return (T) new ConcurrentSkipListMap(); - } - }; - } else if (ConcurrentMap.class.isAssignableFrom(rawType)) { - return new ObjectConstructor() { - @Override public T construct() { - return (T) new ConcurrentHashMap(); - } - }; - } else if (SortedMap.class.isAssignableFrom(rawType)) { - return new ObjectConstructor() { - @Override public T construct() { - return (T) new TreeMap(); - } - }; - } else if (type instanceof ParameterizedType && !(String.class.isAssignableFrom( - TypeToken.get(((ParameterizedType) type).getActualTypeArguments()[0]).getRawType()))) { - return new ObjectConstructor() { - @Override public T construct() { - return (T) new LinkedHashMap(); - } - }; - } else { - return new ObjectConstructor() { - @Override public T construct() { - return (T) new LinkedTreeMap(); - } - }; - } - } - - return null; - } - - private ObjectConstructor newUnsafeAllocator( - final Type type, final Class rawType) { - return new ObjectConstructor() { - private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create(); - @SuppressWarnings("unchecked") - @Override public T construct() { - try { - Object newInstance = unsafeAllocator.newInstance(rawType); - return (T) newInstance; - } catch (Exception e) { - throw new RuntimeException(("Unable to invoke no-args constructor for " + type + ". " - + "Register an InstanceCreator with Gson for this type may fix this problem."), e); - } - } - }; - } - - @Override public String toString() { - return instanceCreators.toString(); - } -} diff --git a/src/gson/main/java/com/google/gson/internal/Excluder.java b/src/gson/main/java/com/google/gson/internal/Excluder.java deleted file mode 100644 index bef7904..0000000 --- a/src/gson/main/java/com/google/gson/internal/Excluder.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal; - -import com.google.gson.ExclusionStrategy; -import com.google.gson.FieldAttributes; -import com.google.gson.Gson; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.annotations.Expose; -import com.google.gson.annotations.Since; -import com.google.gson.annotations.Until; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * This class selects which fields and types to omit. It is configurable, - * supporting version attributes {@link Since} and {@link Until}, modifiers, - * synthetic fields, anonymous and local classes, inner classes, and fields with - * the {@link Expose} annotation. - * - *

This class is a type adapter factory; types that are excluded will be - * adapted to null. It may delegate to another type adapter if only one - * direction is excluded. - * - * @author Joel Leitch - * @author Jesse Wilson - */ -public final class Excluder implements TypeAdapterFactory, Cloneable { - private static final double IGNORE_VERSIONS = -1.0d; - public static final Excluder DEFAULT = new Excluder(); - - private double version = IGNORE_VERSIONS; - private int modifiers = Modifier.TRANSIENT | Modifier.STATIC; - private boolean serializeInnerClasses = true; - private boolean requireExpose; - private List serializationStrategies = Collections.emptyList(); - private List deserializationStrategies = Collections.emptyList(); - - @Override protected Excluder clone() { - try { - return (Excluder) super.clone(); - } catch (CloneNotSupportedException e) { - throw new AssertionError(e); - } - } - - public Excluder withVersion(double ignoreVersionsAfter) { - Excluder result = clone(); - result.version = ignoreVersionsAfter; - return result; - } - - public Excluder withModifiers(int... modifiers) { - Excluder result = clone(); - result.modifiers = 0; - for (int modifier : modifiers) { - result.modifiers |= modifier; - } - return result; - } - - public Excluder disableInnerClassSerialization() { - Excluder result = clone(); - result.serializeInnerClasses = false; - return result; - } - - public Excluder excludeFieldsWithoutExposeAnnotation() { - Excluder result = clone(); - result.requireExpose = true; - return result; - } - - public Excluder withExclusionStrategy(ExclusionStrategy exclusionStrategy, - boolean serialization, boolean deserialization) { - Excluder result = clone(); - if (serialization) { - result.serializationStrategies = new ArrayList(serializationStrategies); - result.serializationStrategies.add(exclusionStrategy); - } - if (deserialization) { - result.deserializationStrategies - = new ArrayList(deserializationStrategies); - result.deserializationStrategies.add(exclusionStrategy); - } - return result; - } - - public TypeAdapter create(final Gson gson, final TypeToken type) { - Class rawType = type.getRawType(); - final boolean skipSerialize = excludeClass(rawType, true); - final boolean skipDeserialize = excludeClass(rawType, false); - - if (!skipSerialize && !skipDeserialize) { - return null; - } - - return new TypeAdapter() { - /** The delegate is lazily created because it may not be needed, and creating it may fail. */ - private TypeAdapter delegate; - - @Override public T read(JsonReader in) throws IOException { - if (skipDeserialize) { - in.skipValue(); - return null; - } - return delegate().read(in); - } - - @Override public void write(JsonWriter out, T value) throws IOException { - if (skipSerialize) { - out.nullValue(); - return; - } - delegate().write(out, value); - } - - private TypeAdapter delegate() { - TypeAdapter d = delegate; - return d != null - ? d - : (delegate = gson.getDelegateAdapter(Excluder.this, type)); - } - }; - } - - public boolean excludeField(Field field, boolean serialize) { - if ((modifiers & field.getModifiers()) != 0) { - return true; - } - - if (version != Excluder.IGNORE_VERSIONS - && !isValidVersion(field.getAnnotation(Since.class), field.getAnnotation(Until.class))) { - return true; - } - - if (field.isSynthetic()) { - return true; - } - - if (requireExpose) { - Expose annotation = field.getAnnotation(Expose.class); - if (annotation == null || (serialize ? !annotation.serialize() : !annotation.deserialize())) { - return true; - } - } - - if (!serializeInnerClasses && isInnerClass(field.getType())) { - return true; - } - - if (isAnonymousOrLocal(field.getType())) { - return true; - } - - List list = serialize ? serializationStrategies : deserializationStrategies; - if (!list.isEmpty()) { - FieldAttributes fieldAttributes = new FieldAttributes(field); - for (ExclusionStrategy exclusionStrategy : list) { - if (exclusionStrategy.shouldSkipField(fieldAttributes)) { - return true; - } - } - } - - return false; - } - - public boolean excludeClass(Class clazz, boolean serialize) { - if (version != Excluder.IGNORE_VERSIONS - && !isValidVersion(clazz.getAnnotation(Since.class), clazz.getAnnotation(Until.class))) { - return true; - } - - if (!serializeInnerClasses && isInnerClass(clazz)) { - return true; - } - - if (isAnonymousOrLocal(clazz)) { - return true; - } - - List list = serialize ? serializationStrategies : deserializationStrategies; - for (ExclusionStrategy exclusionStrategy : list) { - if (exclusionStrategy.shouldSkipClass(clazz)) { - return true; - } - } - - return false; - } - - private boolean isAnonymousOrLocal(Class clazz) { - return !Enum.class.isAssignableFrom(clazz) - && (clazz.isAnonymousClass() || clazz.isLocalClass()); - } - - private boolean isInnerClass(Class clazz) { - return clazz.isMemberClass() && !isStatic(clazz); - } - - private boolean isStatic(Class clazz) { - return (clazz.getModifiers() & Modifier.STATIC) != 0; - } - - private boolean isValidVersion(Since since, Until until) { - return isValidSince(since) && isValidUntil(until); - } - - private boolean isValidSince(Since annotation) { - if (annotation != null) { - double annotationVersion = annotation.value(); - if (annotationVersion > version) { - return false; - } - } - return true; - } - - private boolean isValidUntil(Until annotation) { - if (annotation != null) { - double annotationVersion = annotation.value(); - if (annotationVersion <= version) { - return false; - } - } - return true; - } -} diff --git a/src/gson/main/java/com/google/gson/internal/JsonReaderInternalAccess.java b/src/gson/main/java/com/google/gson/internal/JsonReaderInternalAccess.java deleted file mode 100644 index bbd4720..0000000 --- a/src/gson/main/java/com/google/gson/internal/JsonReaderInternalAccess.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal; - -import com.google.gson.stream.JsonReader; -import java.io.IOException; - -/** - * Internal-only APIs of JsonReader available only to other classes in Gson. - */ -public abstract class JsonReaderInternalAccess { - public static JsonReaderInternalAccess INSTANCE; - - /** - * Changes the type of the current property name token to a string value. - */ - public abstract void promoteNameToValue(JsonReader reader) throws IOException; -} diff --git a/src/gson/main/java/com/google/gson/internal/LazilyParsedNumber.java b/src/gson/main/java/com/google/gson/internal/LazilyParsedNumber.java deleted file mode 100644 index 3669af7..0000000 --- a/src/gson/main/java/com/google/gson/internal/LazilyParsedNumber.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.gson.internal; - -import java.io.ObjectStreamException; -import java.math.BigDecimal; - -/** - * This class holds a number value that is lazily converted to a specific number type - * - * @author Inderjeet Singh - */ -public final class LazilyParsedNumber extends Number { - private final String value; - - /** @param value must not be null */ - public LazilyParsedNumber(String value) { - this.value = value; - } - - @Override - public int intValue() { - try { - return Integer.parseInt(value); - } catch (NumberFormatException e) { - try { - return (int) Long.parseLong(value); - } catch (NumberFormatException nfe) { - return new BigDecimal(value).intValue(); - } - } - } - - @Override - public long longValue() { - try { - return Long.parseLong(value); - } catch (NumberFormatException e) { - return new BigDecimal(value).longValue(); - } - } - - @Override - public float floatValue() { - return Float.parseFloat(value); - } - - @Override - public double doubleValue() { - return Double.parseDouble(value); - } - - @Override - public String toString() { - return value; - } - - /** - * If somebody is unlucky enough to have to serialize one of these, serialize - * it as a BigDecimal so that they won't need Gson on the other side to - * deserialize it. - */ - private Object writeReplace() throws ObjectStreamException { - return new BigDecimal(value); - } - - @Override - public int hashCode() { - return value.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof LazilyParsedNumber) { - LazilyParsedNumber other = (LazilyParsedNumber) obj; - return value == other.value || value.equals(other.value); - } - return false; - } -} diff --git a/src/gson/main/java/com/google/gson/internal/LinkedHashTreeMap.java b/src/gson/main/java/com/google/gson/internal/LinkedHashTreeMap.java deleted file mode 100644 index b2707c5..0000000 --- a/src/gson/main/java/com/google/gson/internal/LinkedHashTreeMap.java +++ /dev/null @@ -1,864 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * Copyright (C) 2012 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal; - -import java.io.ObjectStreamException; -import java.io.Serializable; -import java.util.AbstractMap; -import java.util.AbstractSet; -import java.util.Arrays; -import java.util.Comparator; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.NoSuchElementException; -import java.util.Set; - -/** - * A map of comparable keys to values. Unlike {@code TreeMap}, this class uses - * insertion order for iteration order. Comparison order is only used as an - * optimization for efficient insertion and removal. - * - *

This implementation was derived from Android 4.1's TreeMap and - * LinkedHashMap classes. - */ -public final class LinkedHashTreeMap extends AbstractMap implements Serializable { - @SuppressWarnings({ "unchecked", "rawtypes" }) // to avoid Comparable>> - private static final Comparator NATURAL_ORDER = new Comparator() { - public int compare(Comparable a, Comparable b) { - return a.compareTo(b); - } - }; - - Comparator comparator; - Node[] table; - final Node header; - int size = 0; - int modCount = 0; - int threshold; - - /** - * Create a natural order, empty tree map whose keys must be mutually - * comparable and non-null. - */ - @SuppressWarnings("unchecked") // unsafe! this assumes K is comparable - public LinkedHashTreeMap() { - this((Comparator) NATURAL_ORDER); - } - - /** - * Create a tree map ordered by {@code comparator}. This map's keys may only - * be null if {@code comparator} permits. - * - * @param comparator the comparator to order elements with, or {@code null} to - * use the natural ordering. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) // unsafe! if comparator is null, this assumes K is comparable - public LinkedHashTreeMap(Comparator comparator) { - this.comparator = comparator != null - ? comparator - : (Comparator) NATURAL_ORDER; - this.header = new Node(); - this.table = new Node[16]; // TODO: sizing/resizing policies - this.threshold = (table.length / 2) + (table.length / 4); // 3/4 capacity - } - - @Override public int size() { - return size; - } - - @Override public V get(Object key) { - Node node = findByObject(key); - return node != null ? node.value : null; - } - - @Override public boolean containsKey(Object key) { - return findByObject(key) != null; - } - - @Override public V put(K key, V value) { - if (key == null) { - throw new NullPointerException("key == null"); - } - Node created = find(key, true); - V result = created.value; - created.value = value; - return result; - } - - @Override public void clear() { - Arrays.fill(table, null); - size = 0; - modCount++; - - // Clear all links to help GC - Node header = this.header; - for (Node e = header.next; e != header; ) { - Node next = e.next; - e.next = e.prev = null; - e = next; - } - - header.next = header.prev = header; - } - - @Override public V remove(Object key) { - Node node = removeInternalByKey(key); - return node != null ? node.value : null; - } - - /** - * Returns the node at or adjacent to the given key, creating it if requested. - * - * @throws ClassCastException if {@code key} and the tree's keys aren't - * mutually comparable. - */ - Node find(K key, boolean create) { - Comparator comparator = this.comparator; - Node[] table = this.table; - int hash = secondaryHash(key.hashCode()); - int index = hash & (table.length - 1); - Node nearest = table[index]; - int comparison = 0; - - if (nearest != null) { - // Micro-optimization: avoid polymorphic calls to Comparator.compare(). - @SuppressWarnings("unchecked") // Throws a ClassCastException below if there's trouble. - Comparable comparableKey = (comparator == NATURAL_ORDER) - ? (Comparable) key - : null; - - while (true) { - comparison = (comparableKey != null) - ? comparableKey.compareTo(nearest.key) - : comparator.compare(key, nearest.key); - - // We found the requested key. - if (comparison == 0) { - return nearest; - } - - // If it exists, the key is in a subtree. Go deeper. - Node child = (comparison < 0) ? nearest.left : nearest.right; - if (child == null) { - break; - } - - nearest = child; - } - } - - // The key doesn't exist in this tree. - if (!create) { - return null; - } - - // Create the node and add it to the tree or the table. - Node header = this.header; - Node created; - if (nearest == null) { - // Check that the value is comparable if we didn't do any comparisons. - if (comparator == NATURAL_ORDER && !(key instanceof Comparable)) { - throw new ClassCastException(key.getClass().getName() + " is not Comparable"); - } - created = new Node(nearest, key, hash, header, header.prev); - table[index] = created; - } else { - created = new Node(nearest, key, hash, header, header.prev); - if (comparison < 0) { // nearest.key is higher - nearest.left = created; - } else { // comparison > 0, nearest.key is lower - nearest.right = created; - } - rebalance(nearest, true); - } - - if (size++ > threshold) { - doubleCapacity(); - } - modCount++; - - return created; - } - - @SuppressWarnings("unchecked") - Node findByObject(Object key) { - try { - return key != null ? find((K) key, false) : null; - } catch (ClassCastException e) { - return null; - } - } - - /** - * Returns this map's entry that has the same key and value as {@code - * entry}, or null if this map has no such entry. - * - *

This method uses the comparator for key equality rather than {@code - * equals}. If this map's comparator isn't consistent with equals (such as - * {@code String.CASE_INSENSITIVE_ORDER}), then {@code remove()} and {@code - * contains()} will violate the collections API. - */ - Node findByEntry(Entry entry) { - Node mine = findByObject(entry.getKey()); - boolean valuesEqual = mine != null && equal(mine.value, entry.getValue()); - return valuesEqual ? mine : null; - } - - private boolean equal(Object a, Object b) { - return a == b || (a != null && a.equals(b)); - } - - /** - * Applies a supplemental hash function to a given hashCode, which defends - * against poor quality hash functions. This is critical because HashMap - * uses power-of-two length hash tables, that otherwise encounter collisions - * for hashCodes that do not differ in lower or upper bits. - */ - private static int secondaryHash(int h) { - // Doug Lea's supplemental hash function - h ^= (h >>> 20) ^ (h >>> 12); - return h ^ (h >>> 7) ^ (h >>> 4); - } - - /** - * Removes {@code node} from this tree, rearranging the tree's structure as - * necessary. - * - * @param unlink true to also unlink this node from the iteration linked list. - */ - void removeInternal(Node node, boolean unlink) { - if (unlink) { - node.prev.next = node.next; - node.next.prev = node.prev; - node.next = node.prev = null; // Help the GC (for performance) - } - - Node left = node.left; - Node right = node.right; - Node originalParent = node.parent; - if (left != null && right != null) { - - /* - * To remove a node with both left and right subtrees, move an - * adjacent node from one of those subtrees into this node's place. - * - * Removing the adjacent node may change this node's subtrees. This - * node may no longer have two subtrees once the adjacent node is - * gone! - */ - - Node adjacent = (left.height > right.height) ? left.last() : right.first(); - removeInternal(adjacent, false); // takes care of rebalance and size-- - - int leftHeight = 0; - left = node.left; - if (left != null) { - leftHeight = left.height; - adjacent.left = left; - left.parent = adjacent; - node.left = null; - } - int rightHeight = 0; - right = node.right; - if (right != null) { - rightHeight = right.height; - adjacent.right = right; - right.parent = adjacent; - node.right = null; - } - adjacent.height = Math.max(leftHeight, rightHeight) + 1; - replaceInParent(node, adjacent); - return; - } else if (left != null) { - replaceInParent(node, left); - node.left = null; - } else if (right != null) { - replaceInParent(node, right); - node.right = null; - } else { - replaceInParent(node, null); - } - - rebalance(originalParent, false); - size--; - modCount++; - } - - Node removeInternalByKey(Object key) { - Node node = findByObject(key); - if (node != null) { - removeInternal(node, true); - } - return node; - } - - private void replaceInParent(Node node, Node replacement) { - Node parent = node.parent; - node.parent = null; - if (replacement != null) { - replacement.parent = parent; - } - - if (parent != null) { - if (parent.left == node) { - parent.left = replacement; - } else { - assert (parent.right == node); - parent.right = replacement; - } - } else { - int index = node.hash & (table.length - 1); - table[index] = replacement; - } - } - - /** - * Rebalances the tree by making any AVL rotations necessary between the - * newly-unbalanced node and the tree's root. - * - * @param insert true if the node was unbalanced by an insert; false if it - * was by a removal. - */ - private void rebalance(Node unbalanced, boolean insert) { - for (Node node = unbalanced; node != null; node = node.parent) { - Node left = node.left; - Node right = node.right; - int leftHeight = left != null ? left.height : 0; - int rightHeight = right != null ? right.height : 0; - - int delta = leftHeight - rightHeight; - if (delta == -2) { - Node rightLeft = right.left; - Node rightRight = right.right; - int rightRightHeight = rightRight != null ? rightRight.height : 0; - int rightLeftHeight = rightLeft != null ? rightLeft.height : 0; - - int rightDelta = rightLeftHeight - rightRightHeight; - if (rightDelta == -1 || (rightDelta == 0 && !insert)) { - rotateLeft(node); // AVL right right - } else { - assert (rightDelta == 1); - rotateRight(right); // AVL right left - rotateLeft(node); - } - if (insert) { - break; // no further rotations will be necessary - } - - } else if (delta == 2) { - Node leftLeft = left.left; - Node leftRight = left.right; - int leftRightHeight = leftRight != null ? leftRight.height : 0; - int leftLeftHeight = leftLeft != null ? leftLeft.height : 0; - - int leftDelta = leftLeftHeight - leftRightHeight; - if (leftDelta == 1 || (leftDelta == 0 && !insert)) { - rotateRight(node); // AVL left left - } else { - assert (leftDelta == -1); - rotateLeft(left); // AVL left right - rotateRight(node); - } - if (insert) { - break; // no further rotations will be necessary - } - - } else if (delta == 0) { - node.height = leftHeight + 1; // leftHeight == rightHeight - if (insert) { - break; // the insert caused balance, so rebalancing is done! - } - - } else { - assert (delta == -1 || delta == 1); - node.height = Math.max(leftHeight, rightHeight) + 1; - if (!insert) { - break; // the height hasn't changed, so rebalancing is done! - } - } - } - } - - /** - * Rotates the subtree so that its root's right child is the new root. - */ - private void rotateLeft(Node root) { - Node left = root.left; - Node pivot = root.right; - Node pivotLeft = pivot.left; - Node pivotRight = pivot.right; - - // move the pivot's left child to the root's right - root.right = pivotLeft; - if (pivotLeft != null) { - pivotLeft.parent = root; - } - - replaceInParent(root, pivot); - - // move the root to the pivot's left - pivot.left = root; - root.parent = pivot; - - // fix heights - root.height = Math.max(left != null ? left.height : 0, - pivotLeft != null ? pivotLeft.height : 0) + 1; - pivot.height = Math.max(root.height, - pivotRight != null ? pivotRight.height : 0) + 1; - } - - /** - * Rotates the subtree so that its root's left child is the new root. - */ - private void rotateRight(Node root) { - Node pivot = root.left; - Node right = root.right; - Node pivotLeft = pivot.left; - Node pivotRight = pivot.right; - - // move the pivot's right child to the root's left - root.left = pivotRight; - if (pivotRight != null) { - pivotRight.parent = root; - } - - replaceInParent(root, pivot); - - // move the root to the pivot's right - pivot.right = root; - root.parent = pivot; - - // fixup heights - root.height = Math.max(right != null ? right.height : 0, - pivotRight != null ? pivotRight.height : 0) + 1; - pivot.height = Math.max(root.height, - pivotLeft != null ? pivotLeft.height : 0) + 1; - } - - private EntrySet entrySet; - private KeySet keySet; - - @Override public Set> entrySet() { - EntrySet result = entrySet; - return result != null ? result : (entrySet = new EntrySet()); - } - - @Override public Set keySet() { - KeySet result = keySet; - return result != null ? result : (keySet = new KeySet()); - } - - static final class Node implements Entry { - Node parent; - Node left; - Node right; - Node next; - Node prev; - final K key; - final int hash; - V value; - int height; - - /** Create the header entry */ - Node() { - key = null; - hash = -1; - next = prev = this; - } - - /** Create a regular entry */ - Node(Node parent, K key, int hash, Node next, Node prev) { - this.parent = parent; - this.key = key; - this.hash = hash; - this.height = 1; - this.next = next; - this.prev = prev; - prev.next = this; - next.prev = this; - } - - public K getKey() { - return key; - } - - public V getValue() { - return value; - } - - public V setValue(V value) { - V oldValue = this.value; - this.value = value; - return oldValue; - } - - @SuppressWarnings("rawtypes") - @Override public boolean equals(Object o) { - if (o instanceof Entry) { - Entry other = (Entry) o; - return (key == null ? other.getKey() == null : key.equals(other.getKey())) - && (value == null ? other.getValue() == null : value.equals(other.getValue())); - } - return false; - } - - @Override public int hashCode() { - return (key == null ? 0 : key.hashCode()) - ^ (value == null ? 0 : value.hashCode()); - } - - @Override public String toString() { - return key + "=" + value; - } - - /** - * Returns the first node in this subtree. - */ - public Node first() { - Node node = this; - Node child = node.left; - while (child != null) { - node = child; - child = node.left; - } - return node; - } - - /** - * Returns the last node in this subtree. - */ - public Node last() { - Node node = this; - Node child = node.right; - while (child != null) { - node = child; - child = node.right; - } - return node; - } - } - - private void doubleCapacity() { - table = doubleCapacity(table); - threshold = (table.length / 2) + (table.length / 4); // 3/4 capacity - } - - /** - * Returns a new array containing the same nodes as {@code oldTable}, but with - * twice as many trees, each of (approximately) half the previous size. - */ - static Node[] doubleCapacity(Node[] oldTable) { - // TODO: don't do anything if we're already at MAX_CAPACITY - int oldCapacity = oldTable.length; - @SuppressWarnings("unchecked") // Arrays and generics don't get along. - Node[] newTable = new Node[oldCapacity * 2]; - AvlIterator iterator = new AvlIterator(); - AvlBuilder leftBuilder = new AvlBuilder(); - AvlBuilder rightBuilder = new AvlBuilder(); - - // Split each tree into two trees. - for (int i = 0; i < oldCapacity; i++) { - Node root = oldTable[i]; - if (root == null) { - continue; - } - - // Compute the sizes of the left and right trees. - iterator.reset(root); - int leftSize = 0; - int rightSize = 0; - for (Node node; (node = iterator.next()) != null; ) { - if ((node.hash & oldCapacity) == 0) { - leftSize++; - } else { - rightSize++; - } - } - - // Split the tree into two. - leftBuilder.reset(leftSize); - rightBuilder.reset(rightSize); - iterator.reset(root); - for (Node node; (node = iterator.next()) != null; ) { - if ((node.hash & oldCapacity) == 0) { - leftBuilder.add(node); - } else { - rightBuilder.add(node); - } - } - - // Populate the enlarged array with these new roots. - newTable[i] = leftSize > 0 ? leftBuilder.root() : null; - newTable[i + oldCapacity] = rightSize > 0 ? rightBuilder.root() : null; - } - return newTable; - } - - /** - * Walks an AVL tree in iteration order. Once a node has been returned, its - * left, right and parent links are no longer used. For this - * reason it is safe to transform these links as you walk a tree. - * - *

Warning: this iterator is destructive. It clears the - * parent node of all nodes in the tree. It is an error to make a partial - * iteration of a tree. - */ - static class AvlIterator { - /** This stack is a singly linked list, linked by the 'parent' field. */ - private Node stackTop; - - void reset(Node root) { - Node stackTop = null; - for (Node n = root; n != null; n = n.left) { - n.parent = stackTop; - stackTop = n; // Stack push. - } - this.stackTop = stackTop; - } - - public Node next() { - Node stackTop = this.stackTop; - if (stackTop == null) { - return null; - } - Node result = stackTop; - stackTop = result.parent; - result.parent = null; - for (Node n = result.right; n != null; n = n.left) { - n.parent = stackTop; - stackTop = n; // Stack push. - } - this.stackTop = stackTop; - return result; - } - } - - /** - * Builds AVL trees of a predetermined size by accepting nodes of increasing - * value. To use: - *

    - *
  1. Call {@link #reset} to initialize the target size size. - *
  2. Call {@link #add} size times with increasing values. - *
  3. Call {@link #root} to get the root of the balanced tree. - *
- * - *

The returned tree will satisfy the AVL constraint: for every node - * N, the height of N.left and N.right is different by at - * most 1. It accomplishes this by omitting deepest-level leaf nodes when - * building trees whose size isn't a power of 2 minus 1. - * - *

Unlike rebuilding a tree from scratch, this approach requires no value - * comparisons. Using this class to create a tree of size S is - * {@code O(S)}. - */ - final static class AvlBuilder { - /** This stack is a singly linked list, linked by the 'parent' field. */ - private Node stack; - private int leavesToSkip; - private int leavesSkipped; - private int size; - - void reset(int targetSize) { - // compute the target tree size. This is a power of 2 minus one, like 15 or 31. - int treeCapacity = Integer.highestOneBit(targetSize) * 2 - 1; - leavesToSkip = treeCapacity - targetSize; - size = 0; - leavesSkipped = 0; - stack = null; - } - - void add(Node node) { - node.left = node.parent = node.right = null; - node.height = 1; - - // Skip a leaf if necessary. - if (leavesToSkip > 0 && (size & 1) == 0) { - size++; - leavesToSkip--; - leavesSkipped++; - } - - node.parent = stack; - stack = node; // Stack push. - size++; - - // Skip a leaf if necessary. - if (leavesToSkip > 0 && (size & 1) == 0) { - size++; - leavesToSkip--; - leavesSkipped++; - } - - /* - * Combine 3 nodes into subtrees whenever the size is one less than a - * multiple of 4. For example we combine the nodes A, B, C into a - * 3-element tree with B as the root. - * - * Combine two subtrees and a spare single value whenever the size is one - * less than a multiple of 8. For example at 8 we may combine subtrees - * (A B C) and (E F G) with D as the root to form ((A B C) D (E F G)). - * - * Just as we combine single nodes when size nears a multiple of 4, and - * 3-element trees when size nears a multiple of 8, we combine subtrees of - * size (N-1) whenever the total size is 2N-1 whenever N is a power of 2. - */ - for (int scale = 4; (size & scale - 1) == scale - 1; scale *= 2) { - if (leavesSkipped == 0) { - // Pop right, center and left, then make center the top of the stack. - Node right = stack; - Node center = right.parent; - Node left = center.parent; - center.parent = left.parent; - stack = center; - // Construct a tree. - center.left = left; - center.right = right; - center.height = right.height + 1; - left.parent = center; - right.parent = center; - } else if (leavesSkipped == 1) { - // Pop right and center, then make center the top of the stack. - Node right = stack; - Node center = right.parent; - stack = center; - // Construct a tree with no left child. - center.right = right; - center.height = right.height + 1; - right.parent = center; - leavesSkipped = 0; - } else if (leavesSkipped == 2) { - leavesSkipped = 0; - } - } - } - - Node root() { - Node stackTop = this.stack; - if (stackTop.parent != null) { - throw new IllegalStateException(); - } - return stackTop; - } - } - - private abstract class LinkedTreeMapIterator implements Iterator { - Node next = header.next; - Node lastReturned = null; - int expectedModCount = modCount; - - LinkedTreeMapIterator() { - } - - public final boolean hasNext() { - return next != header; - } - - final Node nextNode() { - Node e = next; - if (e == header) { - throw new NoSuchElementException(); - } - if (modCount != expectedModCount) { - throw new ConcurrentModificationException(); - } - next = e.next; - return lastReturned = e; - } - - public final void remove() { - if (lastReturned == null) { - throw new IllegalStateException(); - } - removeInternal(lastReturned, true); - lastReturned = null; - expectedModCount = modCount; - } - } - - final class EntrySet extends AbstractSet> { - @Override public int size() { - return size; - } - - @Override public Iterator> iterator() { - return new LinkedTreeMapIterator>() { - public Entry next() { - return nextNode(); - } - }; - } - - @Override public boolean contains(Object o) { - return o instanceof Entry && findByEntry((Entry) o) != null; - } - - @Override public boolean remove(Object o) { - if (!(o instanceof Entry)) { - return false; - } - - Node node = findByEntry((Entry) o); - if (node == null) { - return false; - } - removeInternal(node, true); - return true; - } - - @Override public void clear() { - LinkedHashTreeMap.this.clear(); - } - } - - final class KeySet extends AbstractSet { - @Override public int size() { - return size; - } - - @Override public Iterator iterator() { - return new LinkedTreeMapIterator() { - public K next() { - return nextNode().key; - } - }; - } - - @Override public boolean contains(Object o) { - return containsKey(o); - } - - @Override public boolean remove(Object key) { - return removeInternalByKey(key) != null; - } - - @Override public void clear() { - LinkedHashTreeMap.this.clear(); - } - } - - /** - * If somebody is unlucky enough to have to serialize one of these, serialize - * it as a LinkedHashMap so that they won't need Gson on the other side to - * deserialize it. Using serialization defeats our DoS defence, so most apps - * shouldn't use it. - */ - private Object writeReplace() throws ObjectStreamException { - return new LinkedHashMap(this); - } -} diff --git a/src/gson/main/java/com/google/gson/internal/LinkedTreeMap.java b/src/gson/main/java/com/google/gson/internal/LinkedTreeMap.java deleted file mode 100644 index 8046274..0000000 --- a/src/gson/main/java/com/google/gson/internal/LinkedTreeMap.java +++ /dev/null @@ -1,630 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * Copyright (C) 2012 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal; - -import java.io.ObjectStreamException; -import java.io.Serializable; -import java.util.AbstractMap; -import java.util.AbstractSet; -import java.util.Comparator; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.NoSuchElementException; -import java.util.Set; - -/** - * A map of comparable keys to values. Unlike {@code TreeMap}, this class uses - * insertion order for iteration order. Comparison order is only used as an - * optimization for efficient insertion and removal. - * - *

This implementation was derived from Android 4.1's TreeMap class. - */ -public final class LinkedTreeMap extends AbstractMap implements Serializable { - @SuppressWarnings({ "unchecked", "rawtypes" }) // to avoid Comparable>> - private static final Comparator NATURAL_ORDER = new Comparator() { - public int compare(Comparable a, Comparable b) { - return a.compareTo(b); - } - }; - - Comparator comparator; - Node root; - int size = 0; - int modCount = 0; - - // Used to preserve iteration order - final Node header = new Node(); - - /** - * Create a natural order, empty tree map whose keys must be mutually - * comparable and non-null. - */ - @SuppressWarnings("unchecked") // unsafe! this assumes K is comparable - public LinkedTreeMap() { - this((Comparator) NATURAL_ORDER); - } - - /** - * Create a tree map ordered by {@code comparator}. This map's keys may only - * be null if {@code comparator} permits. - * - * @param comparator the comparator to order elements with, or {@code null} to - * use the natural ordering. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) // unsafe! if comparator is null, this assumes K is comparable - public LinkedTreeMap(Comparator comparator) { - this.comparator = comparator != null - ? comparator - : (Comparator) NATURAL_ORDER; - } - - @Override public int size() { - return size; - } - - @Override public V get(Object key) { - Node node = findByObject(key); - return node != null ? node.value : null; - } - - @Override public boolean containsKey(Object key) { - return findByObject(key) != null; - } - - @Override public V put(K key, V value) { - if (key == null) { - throw new NullPointerException("key == null"); - } - Node created = find(key, true); - V result = created.value; - created.value = value; - return result; - } - - @Override public void clear() { - root = null; - size = 0; - modCount++; - - // Clear iteration order - Node header = this.header; - header.next = header.prev = header; - } - - @Override public V remove(Object key) { - Node node = removeInternalByKey(key); - return node != null ? node.value : null; - } - - /** - * Returns the node at or adjacent to the given key, creating it if requested. - * - * @throws ClassCastException if {@code key} and the tree's keys aren't - * mutually comparable. - */ - Node find(K key, boolean create) { - Comparator comparator = this.comparator; - Node nearest = root; - int comparison = 0; - - if (nearest != null) { - // Micro-optimization: avoid polymorphic calls to Comparator.compare(). - @SuppressWarnings("unchecked") // Throws a ClassCastException below if there's trouble. - Comparable comparableKey = (comparator == NATURAL_ORDER) - ? (Comparable) key - : null; - - while (true) { - comparison = (comparableKey != null) - ? comparableKey.compareTo(nearest.key) - : comparator.compare(key, nearest.key); - - // We found the requested key. - if (comparison == 0) { - return nearest; - } - - // If it exists, the key is in a subtree. Go deeper. - Node child = (comparison < 0) ? nearest.left : nearest.right; - if (child == null) { - break; - } - - nearest = child; - } - } - - // The key doesn't exist in this tree. - if (!create) { - return null; - } - - // Create the node and add it to the tree or the table. - Node header = this.header; - Node created; - if (nearest == null) { - // Check that the value is comparable if we didn't do any comparisons. - if (comparator == NATURAL_ORDER && !(key instanceof Comparable)) { - throw new ClassCastException(key.getClass().getName() + " is not Comparable"); - } - created = new Node(nearest, key, header, header.prev); - root = created; - } else { - created = new Node(nearest, key, header, header.prev); - if (comparison < 0) { // nearest.key is higher - nearest.left = created; - } else { // comparison > 0, nearest.key is lower - nearest.right = created; - } - rebalance(nearest, true); - } - size++; - modCount++; - - return created; - } - - @SuppressWarnings("unchecked") - Node findByObject(Object key) { - try { - return key != null ? find((K) key, false) : null; - } catch (ClassCastException e) { - return null; - } - } - - /** - * Returns this map's entry that has the same key and value as {@code - * entry}, or null if this map has no such entry. - * - *

This method uses the comparator for key equality rather than {@code - * equals}. If this map's comparator isn't consistent with equals (such as - * {@code String.CASE_INSENSITIVE_ORDER}), then {@code remove()} and {@code - * contains()} will violate the collections API. - */ - Node findByEntry(Entry entry) { - Node mine = findByObject(entry.getKey()); - boolean valuesEqual = mine != null && equal(mine.value, entry.getValue()); - return valuesEqual ? mine : null; - } - - private boolean equal(Object a, Object b) { - return a == b || (a != null && a.equals(b)); - } - - /** - * Removes {@code node} from this tree, rearranging the tree's structure as - * necessary. - * - * @param unlink true to also unlink this node from the iteration linked list. - */ - void removeInternal(Node node, boolean unlink) { - if (unlink) { - node.prev.next = node.next; - node.next.prev = node.prev; - } - - Node left = node.left; - Node right = node.right; - Node originalParent = node.parent; - if (left != null && right != null) { - - /* - * To remove a node with both left and right subtrees, move an - * adjacent node from one of those subtrees into this node's place. - * - * Removing the adjacent node may change this node's subtrees. This - * node may no longer have two subtrees once the adjacent node is - * gone! - */ - - Node adjacent = (left.height > right.height) ? left.last() : right.first(); - removeInternal(adjacent, false); // takes care of rebalance and size-- - - int leftHeight = 0; - left = node.left; - if (left != null) { - leftHeight = left.height; - adjacent.left = left; - left.parent = adjacent; - node.left = null; - } - - int rightHeight = 0; - right = node.right; - if (right != null) { - rightHeight = right.height; - adjacent.right = right; - right.parent = adjacent; - node.right = null; - } - - adjacent.height = Math.max(leftHeight, rightHeight) + 1; - replaceInParent(node, adjacent); - return; - } else if (left != null) { - replaceInParent(node, left); - node.left = null; - } else if (right != null) { - replaceInParent(node, right); - node.right = null; - } else { - replaceInParent(node, null); - } - - rebalance(originalParent, false); - size--; - modCount++; - } - - Node removeInternalByKey(Object key) { - Node node = findByObject(key); - if (node != null) { - removeInternal(node, true); - } - return node; - } - - private void replaceInParent(Node node, Node replacement) { - Node parent = node.parent; - node.parent = null; - if (replacement != null) { - replacement.parent = parent; - } - - if (parent != null) { - if (parent.left == node) { - parent.left = replacement; - } else { - assert (parent.right == node); - parent.right = replacement; - } - } else { - root = replacement; - } - } - - /** - * Rebalances the tree by making any AVL rotations necessary between the - * newly-unbalanced node and the tree's root. - * - * @param insert true if the node was unbalanced by an insert; false if it - * was by a removal. - */ - private void rebalance(Node unbalanced, boolean insert) { - for (Node node = unbalanced; node != null; node = node.parent) { - Node left = node.left; - Node right = node.right; - int leftHeight = left != null ? left.height : 0; - int rightHeight = right != null ? right.height : 0; - - int delta = leftHeight - rightHeight; - if (delta == -2) { - Node rightLeft = right.left; - Node rightRight = right.right; - int rightRightHeight = rightRight != null ? rightRight.height : 0; - int rightLeftHeight = rightLeft != null ? rightLeft.height : 0; - - int rightDelta = rightLeftHeight - rightRightHeight; - if (rightDelta == -1 || (rightDelta == 0 && !insert)) { - rotateLeft(node); // AVL right right - } else { - assert (rightDelta == 1); - rotateRight(right); // AVL right left - rotateLeft(node); - } - if (insert) { - break; // no further rotations will be necessary - } - - } else if (delta == 2) { - Node leftLeft = left.left; - Node leftRight = left.right; - int leftRightHeight = leftRight != null ? leftRight.height : 0; - int leftLeftHeight = leftLeft != null ? leftLeft.height : 0; - - int leftDelta = leftLeftHeight - leftRightHeight; - if (leftDelta == 1 || (leftDelta == 0 && !insert)) { - rotateRight(node); // AVL left left - } else { - assert (leftDelta == -1); - rotateLeft(left); // AVL left right - rotateRight(node); - } - if (insert) { - break; // no further rotations will be necessary - } - - } else if (delta == 0) { - node.height = leftHeight + 1; // leftHeight == rightHeight - if (insert) { - break; // the insert caused balance, so rebalancing is done! - } - - } else { - assert (delta == -1 || delta == 1); - node.height = Math.max(leftHeight, rightHeight) + 1; - if (!insert) { - break; // the height hasn't changed, so rebalancing is done! - } - } - } - } - - /** - * Rotates the subtree so that its root's right child is the new root. - */ - private void rotateLeft(Node root) { - Node left = root.left; - Node pivot = root.right; - Node pivotLeft = pivot.left; - Node pivotRight = pivot.right; - - // move the pivot's left child to the root's right - root.right = pivotLeft; - if (pivotLeft != null) { - pivotLeft.parent = root; - } - - replaceInParent(root, pivot); - - // move the root to the pivot's left - pivot.left = root; - root.parent = pivot; - - // fix heights - root.height = Math.max(left != null ? left.height : 0, - pivotLeft != null ? pivotLeft.height : 0) + 1; - pivot.height = Math.max(root.height, - pivotRight != null ? pivotRight.height : 0) + 1; - } - - /** - * Rotates the subtree so that its root's left child is the new root. - */ - private void rotateRight(Node root) { - Node pivot = root.left; - Node right = root.right; - Node pivotLeft = pivot.left; - Node pivotRight = pivot.right; - - // move the pivot's right child to the root's left - root.left = pivotRight; - if (pivotRight != null) { - pivotRight.parent = root; - } - - replaceInParent(root, pivot); - - // move the root to the pivot's right - pivot.right = root; - root.parent = pivot; - - // fixup heights - root.height = Math.max(right != null ? right.height : 0, - pivotRight != null ? pivotRight.height : 0) + 1; - pivot.height = Math.max(root.height, - pivotLeft != null ? pivotLeft.height : 0) + 1; - } - - private EntrySet entrySet; - private KeySet keySet; - - @Override public Set> entrySet() { - EntrySet result = entrySet; - return result != null ? result : (entrySet = new EntrySet()); - } - - @Override public Set keySet() { - KeySet result = keySet; - return result != null ? result : (keySet = new KeySet()); - } - - static final class Node implements Entry { - Node parent; - Node left; - Node right; - Node next; - Node prev; - final K key; - V value; - int height; - - /** Create the header entry */ - Node() { - key = null; - next = prev = this; - } - - /** Create a regular entry */ - Node(Node parent, K key, Node next, Node prev) { - this.parent = parent; - this.key = key; - this.height = 1; - this.next = next; - this.prev = prev; - prev.next = this; - next.prev = this; - } - - public K getKey() { - return key; - } - - public V getValue() { - return value; - } - - public V setValue(V value) { - V oldValue = this.value; - this.value = value; - return oldValue; - } - - @SuppressWarnings("rawtypes") - @Override public boolean equals(Object o) { - if (o instanceof Entry) { - Entry other = (Entry) o; - return (key == null ? other.getKey() == null : key.equals(other.getKey())) - && (value == null ? other.getValue() == null : value.equals(other.getValue())); - } - return false; - } - - @Override public int hashCode() { - return (key == null ? 0 : key.hashCode()) - ^ (value == null ? 0 : value.hashCode()); - } - - @Override public String toString() { - return key + "=" + value; - } - - /** - * Returns the first node in this subtree. - */ - public Node first() { - Node node = this; - Node child = node.left; - while (child != null) { - node = child; - child = node.left; - } - return node; - } - - /** - * Returns the last node in this subtree. - */ - public Node last() { - Node node = this; - Node child = node.right; - while (child != null) { - node = child; - child = node.right; - } - return node; - } - } - - private abstract class LinkedTreeMapIterator implements Iterator { - Node next = header.next; - Node lastReturned = null; - int expectedModCount = modCount; - - LinkedTreeMapIterator() { - } - - public final boolean hasNext() { - return next != header; - } - - final Node nextNode() { - Node e = next; - if (e == header) { - throw new NoSuchElementException(); - } - if (modCount != expectedModCount) { - throw new ConcurrentModificationException(); - } - next = e.next; - return lastReturned = e; - } - - public final void remove() { - if (lastReturned == null) { - throw new IllegalStateException(); - } - removeInternal(lastReturned, true); - lastReturned = null; - expectedModCount = modCount; - } - } - - class EntrySet extends AbstractSet> { - @Override public int size() { - return size; - } - - @Override public Iterator> iterator() { - return new LinkedTreeMapIterator>() { - public Entry next() { - return nextNode(); - } - }; - } - - @Override public boolean contains(Object o) { - return o instanceof Entry && findByEntry((Entry) o) != null; - } - - @Override public boolean remove(Object o) { - if (!(o instanceof Entry)) { - return false; - } - - Node node = findByEntry((Entry) o); - if (node == null) { - return false; - } - removeInternal(node, true); - return true; - } - - @Override public void clear() { - LinkedTreeMap.this.clear(); - } - } - - final class KeySet extends AbstractSet { - @Override public int size() { - return size; - } - - @Override public Iterator iterator() { - return new LinkedTreeMapIterator() { - public K next() { - return nextNode().key; - } - }; - } - - @Override public boolean contains(Object o) { - return containsKey(o); - } - - @Override public boolean remove(Object key) { - return removeInternalByKey(key) != null; - } - - @Override public void clear() { - LinkedTreeMap.this.clear(); - } - } - - /** - * If somebody is unlucky enough to have to serialize one of these, serialize - * it as a LinkedHashMap so that they won't need Gson on the other side to - * deserialize it. Using serialization defeats our DoS defence, so most apps - * shouldn't use it. - */ - private Object writeReplace() throws ObjectStreamException { - return new LinkedHashMap(this); - } -} diff --git a/src/gson/main/java/com/google/gson/internal/ObjectConstructor.java b/src/gson/main/java/com/google/gson/internal/ObjectConstructor.java deleted file mode 100644 index 6ef2060..0000000 --- a/src/gson/main/java/com/google/gson/internal/ObjectConstructor.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal; - -/** - * Defines a generic object construction factory. The purpose of this class - * is to construct a default instance of a class that can be used for object - * navigation while deserialization from its JSON representation. - * - * @author Inderjeet Singh - * @author Joel Leitch - */ -public interface ObjectConstructor { - - /** - * Returns a new instance. - */ - public T construct(); -} \ No newline at end of file diff --git a/src/gson/main/java/com/google/gson/internal/Primitives.java b/src/gson/main/java/com/google/gson/internal/Primitives.java deleted file mode 100644 index a98f624..0000000 --- a/src/gson/main/java/com/google/gson/internal/Primitives.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal; - - -import java.lang.reflect.Type; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -/** - * Contains static utility methods pertaining to primitive types and their - * corresponding wrapper types. - * - * @author Kevin Bourrillion - */ -public final class Primitives { - private Primitives() { - throw new UnsupportedOperationException(); - } - - /** A map from primitive types to their corresponding wrapper types. */ - private static final Map, Class> PRIMITIVE_TO_WRAPPER_TYPE; - - /** A map from wrapper types to their corresponding primitive types. */ - private static final Map, Class> WRAPPER_TO_PRIMITIVE_TYPE; - - // Sad that we can't use a BiMap. :( - - static { - Map, Class> primToWrap = new HashMap, Class>(16); - Map, Class> wrapToPrim = new HashMap, Class>(16); - - add(primToWrap, wrapToPrim, boolean.class, Boolean.class); - add(primToWrap, wrapToPrim, byte.class, Byte.class); - add(primToWrap, wrapToPrim, char.class, Character.class); - add(primToWrap, wrapToPrim, double.class, Double.class); - add(primToWrap, wrapToPrim, float.class, Float.class); - add(primToWrap, wrapToPrim, int.class, Integer.class); - add(primToWrap, wrapToPrim, long.class, Long.class); - add(primToWrap, wrapToPrim, short.class, Short.class); - add(primToWrap, wrapToPrim, void.class, Void.class); - - PRIMITIVE_TO_WRAPPER_TYPE = Collections.unmodifiableMap(primToWrap); - WRAPPER_TO_PRIMITIVE_TYPE = Collections.unmodifiableMap(wrapToPrim); - } - - private static void add(Map, Class> forward, - Map, Class> backward, Class key, Class value) { - forward.put(key, value); - backward.put(value, key); - } - - /** - * Returns true if this type is a primitive. - */ - public static boolean isPrimitive(Type type) { - return PRIMITIVE_TO_WRAPPER_TYPE.containsKey(type); - } - - /** - * Returns {@code true} if {@code type} is one of the nine - * primitive-wrapper types, such as {@link Integer}. - * - * @see Class#isPrimitive - */ - public static boolean isWrapperType(Type type) { - return WRAPPER_TO_PRIMITIVE_TYPE.containsKey( - $Gson$Preconditions.checkNotNull(type)); - } - - /** - * Returns the corresponding wrapper type of {@code type} if it is a primitive - * type; otherwise returns {@code type} itself. Idempotent. - *

-   *     wrap(int.class) == Integer.class
-   *     wrap(Integer.class) == Integer.class
-   *     wrap(String.class) == String.class
-   * 
- */ - public static Class wrap(Class type) { - // cast is safe: long.class and Long.class are both of type Class - @SuppressWarnings("unchecked") - Class wrapped = (Class) PRIMITIVE_TO_WRAPPER_TYPE.get( - $Gson$Preconditions.checkNotNull(type)); - return (wrapped == null) ? type : wrapped; - } - - /** - * Returns the corresponding primitive type of {@code type} if it is a - * wrapper type; otherwise returns {@code type} itself. Idempotent. - *
-   *     unwrap(Integer.class) == int.class
-   *     unwrap(int.class) == int.class
-   *     unwrap(String.class) == String.class
-   * 
- */ - public static Class unwrap(Class type) { - // cast is safe: long.class and Long.class are both of type Class - @SuppressWarnings("unchecked") - Class unwrapped = (Class) WRAPPER_TO_PRIMITIVE_TYPE.get( - $Gson$Preconditions.checkNotNull(type)); - return (unwrapped == null) ? type : unwrapped; - } -} diff --git a/src/gson/main/java/com/google/gson/internal/Streams.java b/src/gson/main/java/com/google/gson/internal/Streams.java deleted file mode 100644 index ac99910..0000000 --- a/src/gson/main/java/com/google/gson/internal/Streams.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal; - -import com.google.gson.JsonElement; -import com.google.gson.JsonIOException; -import com.google.gson.JsonNull; -import com.google.gson.JsonParseException; -import com.google.gson.JsonSyntaxException; -import com.google.gson.internal.bind.TypeAdapters; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import com.google.gson.stream.MalformedJsonException; -import java.io.EOFException; -import java.io.IOException; -import java.io.Writer; - -/** - * Reads and writes GSON parse trees over streams. - */ -public final class Streams { - private Streams() { - throw new UnsupportedOperationException(); - } - - /** - * Takes a reader in any state and returns the next value as a JsonElement. - */ - public static JsonElement parse(JsonReader reader) throws JsonParseException { - boolean isEmpty = true; - try { - reader.peek(); - isEmpty = false; - return TypeAdapters.JSON_ELEMENT.read(reader); - } catch (EOFException e) { - /* - * For compatibility with JSON 1.5 and earlier, we return a JsonNull for - * empty documents instead of throwing. - */ - if (isEmpty) { - return JsonNull.INSTANCE; - } - // The stream ended prematurely so it is likely a syntax error. - throw new JsonSyntaxException(e); - } catch (MalformedJsonException e) { - throw new JsonSyntaxException(e); - } catch (IOException e) { - throw new JsonIOException(e); - } catch (NumberFormatException e) { - throw new JsonSyntaxException(e); - } - } - - /** - * Writes the JSON element to the writer, recursively. - */ - public static void write(JsonElement element, JsonWriter writer) throws IOException { - TypeAdapters.JSON_ELEMENT.write(writer, element); - } - - public static Writer writerForAppendable(Appendable appendable) { - return appendable instanceof Writer ? (Writer) appendable : new AppendableWriter(appendable); - } - - /** - * Adapts an {@link Appendable} so it can be passed anywhere a {@link Writer} - * is used. - */ - private static final class AppendableWriter extends Writer { - private final Appendable appendable; - private final CurrentWrite currentWrite = new CurrentWrite(); - - AppendableWriter(Appendable appendable) { - this.appendable = appendable; - } - - @Override public void write(char[] chars, int offset, int length) throws IOException { - currentWrite.chars = chars; - appendable.append(currentWrite, offset, offset + length); - } - - @Override public void write(int i) throws IOException { - appendable.append((char) i); - } - - @Override public void flush() {} - @Override public void close() {} - - /** - * A mutable char sequence pointing at a single char[]. - */ - static class CurrentWrite implements CharSequence { - char[] chars; - public int length() { - return chars.length; - } - public char charAt(int i) { - return chars[i]; - } - public CharSequence subSequence(int start, int end) { - return new String(chars, start, end - start); - } - } - } - -} diff --git a/src/gson/main/java/com/google/gson/internal/UnsafeAllocator.java b/src/gson/main/java/com/google/gson/internal/UnsafeAllocator.java deleted file mode 100644 index 999a2b5..0000000 --- a/src/gson/main/java/com/google/gson/internal/UnsafeAllocator.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal; - -import java.io.ObjectInputStream; -import java.io.ObjectStreamClass; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; - -/** - * Do sneaky things to allocate objects without invoking their constructors. - * - * @author Joel Leitch - * @author Jesse Wilson - */ -public abstract class UnsafeAllocator { - public abstract T newInstance(Class c) throws Exception; - - public static UnsafeAllocator create() { - // try JVM - // public class Unsafe { - // public Object allocateInstance(Class type); - // } - try { - Class unsafeClass = Class.forName("sun.misc.Unsafe"); - Field f = unsafeClass.getDeclaredField("theUnsafe"); - f.setAccessible(true); - final Object unsafe = f.get(null); - final Method allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class); - return new UnsafeAllocator() { - @Override - @SuppressWarnings("unchecked") - public T newInstance(Class c) throws Exception { - assertInstantiable(c); - return (T) allocateInstance.invoke(unsafe, c); - } - }; - } catch (Exception ignored) { - } - - // try dalvikvm, post-gingerbread - // public class ObjectStreamClass { - // private static native int getConstructorId(Class c); - // private static native Object newInstance(Class instantiationClass, int methodId); - // } - try { - Method getConstructorId = ObjectStreamClass.class - .getDeclaredMethod("getConstructorId", Class.class); - getConstructorId.setAccessible(true); - final int constructorId = (Integer) getConstructorId.invoke(null, Object.class); - final Method newInstance = ObjectStreamClass.class - .getDeclaredMethod("newInstance", Class.class, int.class); - newInstance.setAccessible(true); - return new UnsafeAllocator() { - @Override - @SuppressWarnings("unchecked") - public T newInstance(Class c) throws Exception { - assertInstantiable(c); - return (T) newInstance.invoke(null, c, constructorId); - } - }; - } catch (Exception ignored) { - } - - // try dalvikvm, pre-gingerbread - // public class ObjectInputStream { - // private static native Object newInstance( - // Class instantiationClass, Class constructorClass); - // } - try { - final Method newInstance = ObjectInputStream.class - .getDeclaredMethod("newInstance", Class.class, Class.class); - newInstance.setAccessible(true); - return new UnsafeAllocator() { - @Override - @SuppressWarnings("unchecked") - public T newInstance(Class c) throws Exception { - assertInstantiable(c); - return (T) newInstance.invoke(null, c, Object.class); - } - }; - } catch (Exception ignored) { - } - - // give up - return new UnsafeAllocator() { - @Override - public T newInstance(Class c) { - throw new UnsupportedOperationException("Cannot allocate " + c); - } - }; - } - - /** - * Check if the class can be instantiated by unsafe allocator. If the instance has interface or abstract modifiers - * throw an {@link java.lang.UnsupportedOperationException} - * @param c instance of the class to be checked - */ - static void assertInstantiable(Class c) { - int modifiers = c.getModifiers(); - if (Modifier.isInterface(modifiers)) { - throw new UnsupportedOperationException("Interface can't be instantiated! Interface name: " + c.getName()); - } - if (Modifier.isAbstract(modifiers)) { - throw new UnsupportedOperationException("Abstract class can't be instantiated! Class name: " + c.getName()); - } - } -} diff --git a/src/gson/main/java/com/google/gson/internal/bind/ArrayTypeAdapter.java b/src/gson/main/java/com/google/gson/internal/bind/ArrayTypeAdapter.java deleted file mode 100644 index 5610170..0000000 --- a/src/gson/main/java/com/google/gson/internal/bind/ArrayTypeAdapter.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal.bind; - -import java.io.IOException; -import java.lang.reflect.Array; -import java.lang.reflect.GenericArrayType; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.List; - -import com.google.gson.Gson; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.internal.$Gson$Types; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; - -/** - * Adapt an array of objects. - */ -public final class ArrayTypeAdapter extends TypeAdapter { - public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { - @SuppressWarnings({"unchecked", "rawtypes"}) - @Override public TypeAdapter create(Gson gson, TypeToken typeToken) { - Type type = typeToken.getType(); - if (!(type instanceof GenericArrayType || type instanceof Class && ((Class) type).isArray())) { - return null; - } - - Type componentType = $Gson$Types.getArrayComponentType(type); - TypeAdapter componentTypeAdapter = gson.getAdapter(TypeToken.get(componentType)); - return new ArrayTypeAdapter( - gson, componentTypeAdapter, $Gson$Types.getRawType(componentType)); - } - }; - - private final Class componentType; - private final TypeAdapter componentTypeAdapter; - - public ArrayTypeAdapter(Gson context, TypeAdapter componentTypeAdapter, Class componentType) { - this.componentTypeAdapter = - new TypeAdapterRuntimeTypeWrapper(context, componentTypeAdapter, componentType); - this.componentType = componentType; - } - - @Override public Object read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - - List list = new ArrayList(); - in.beginArray(); - while (in.hasNext()) { - E instance = componentTypeAdapter.read(in); - list.add(instance); - } - in.endArray(); - - int size = list.size(); - Object array = Array.newInstance(componentType, size); - for (int i = 0; i < size; i++) { - Array.set(array, i, list.get(i)); - } - return array; - } - - @SuppressWarnings("unchecked") - @Override public void write(JsonWriter out, Object array) throws IOException { - if (array == null) { - out.nullValue(); - return; - } - - out.beginArray(); - for (int i = 0, length = Array.getLength(array); i < length; i++) { - E value = (E) Array.get(array, i); - componentTypeAdapter.write(out, value); - } - out.endArray(); - } -} diff --git a/src/gson/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java b/src/gson/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java deleted file mode 100644 index 1d57844..0000000 --- a/src/gson/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal.bind; - -import com.google.gson.Gson; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.internal.$Gson$Types; -import com.google.gson.internal.ConstructorConstructor; -import com.google.gson.internal.ObjectConstructor; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.lang.reflect.Type; -import java.util.Collection; - -/** - * Adapt a homogeneous collection of objects. - */ -public final class CollectionTypeAdapterFactory implements TypeAdapterFactory { - private final ConstructorConstructor constructorConstructor; - - public CollectionTypeAdapterFactory(ConstructorConstructor constructorConstructor) { - this.constructorConstructor = constructorConstructor; - } - - @Override - public TypeAdapter create(Gson gson, TypeToken typeToken) { - Type type = typeToken.getType(); - - Class rawType = typeToken.getRawType(); - if (!Collection.class.isAssignableFrom(rawType)) { - return null; - } - - Type elementType = $Gson$Types.getCollectionElementType(type, rawType); - TypeAdapter elementTypeAdapter = gson.getAdapter(TypeToken.get(elementType)); - ObjectConstructor constructor = constructorConstructor.get(typeToken); - - @SuppressWarnings({"unchecked", "rawtypes"}) // create() doesn't define a type parameter - TypeAdapter result = new Adapter(gson, elementType, elementTypeAdapter, constructor); - return result; - } - - private static final class Adapter extends TypeAdapter> { - private final TypeAdapter elementTypeAdapter; - private final ObjectConstructor> constructor; - - public Adapter(Gson context, Type elementType, - TypeAdapter elementTypeAdapter, - ObjectConstructor> constructor) { - this.elementTypeAdapter = - new TypeAdapterRuntimeTypeWrapper(context, elementTypeAdapter, elementType); - this.constructor = constructor; - } - - @Override public Collection read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - - Collection collection = constructor.construct(); - in.beginArray(); - while (in.hasNext()) { - E instance = elementTypeAdapter.read(in); - collection.add(instance); - } - in.endArray(); - return collection; - } - - @Override public void write(JsonWriter out, Collection collection) throws IOException { - if (collection == null) { - out.nullValue(); - return; - } - - out.beginArray(); - for (E element : collection) { - elementTypeAdapter.write(out, element); - } - out.endArray(); - } - } -} diff --git a/src/gson/main/java/com/google/gson/internal/bind/DateTypeAdapter.java b/src/gson/main/java/com/google/gson/internal/bind/DateTypeAdapter.java deleted file mode 100644 index 561af19..0000000 --- a/src/gson/main/java/com/google/gson/internal/bind/DateTypeAdapter.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal.bind; - -import com.google.gson.Gson; -import com.google.gson.JsonSyntaxException; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.internal.bind.util.ISO8601Utils; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.ParsePosition; -import java.util.Date; -import java.util.Locale; - -/** - * Adapter for Date. Although this class appears stateless, it is not. - * DateFormat captures its time zone and locale when it is created, which gives - * this class state. DateFormat isn't thread safe either, so this class has - * to synchronize its read and write methods. - */ -public final class DateTypeAdapter extends TypeAdapter { - public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { - @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal - @Override public TypeAdapter create(Gson gson, TypeToken typeToken) { - return typeToken.getRawType() == Date.class ? (TypeAdapter) new DateTypeAdapter() : null; - } - }; - - private final DateFormat enUsFormat - = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US); - private final DateFormat localFormat - = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT); - - @Override public Date read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - return deserializeToDate(in.nextString()); - } - - private synchronized Date deserializeToDate(String json) { - try { - return localFormat.parse(json); - } catch (ParseException ignored) { - } - try { - return enUsFormat.parse(json); - } catch (ParseException ignored) { - } - try { - return ISO8601Utils.parse(json, new ParsePosition(0)); - } catch (ParseException e) { - throw new JsonSyntaxException(json, e); - } - } - - @Override public synchronized void write(JsonWriter out, Date value) throws IOException { - if (value == null) { - out.nullValue(); - return; - } - String dateFormatAsString = enUsFormat.format(value); - out.value(dateFormatAsString); - } - - -} diff --git a/src/gson/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java b/src/gson/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java deleted file mode 100644 index 13a7bb7..0000000 --- a/src/gson/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2014 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal.bind; - -import com.google.gson.Gson; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonSerializer; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.internal.ConstructorConstructor; -import com.google.gson.reflect.TypeToken; - -/** - * Given a type T, looks for the annotation {@link JsonAdapter} and uses an instance of the - * specified class as the default type adapter. - * - * @since 2.3 - */ -public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapterFactory { - private final ConstructorConstructor constructorConstructor; - - public JsonAdapterAnnotationTypeAdapterFactory(ConstructorConstructor constructorConstructor) { - this.constructorConstructor = constructorConstructor; - } - - @SuppressWarnings("unchecked") - @Override - public TypeAdapter create(Gson gson, TypeToken targetType) { - Class rawType = targetType.getRawType(); - JsonAdapter annotation = rawType.getAnnotation(JsonAdapter.class); - if (annotation == null) { - return null; - } - return (TypeAdapter) getTypeAdapter(constructorConstructor, gson, targetType, annotation); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) // Casts guarded by conditionals. - TypeAdapter getTypeAdapter(ConstructorConstructor constructorConstructor, Gson gson, - TypeToken type, JsonAdapter annotation) { - Object instance = constructorConstructor.get(TypeToken.get(annotation.value())).construct(); - - TypeAdapter typeAdapter; - if (instance instanceof TypeAdapter) { - typeAdapter = (TypeAdapter) instance; - } else if (instance instanceof TypeAdapterFactory) { - typeAdapter = ((TypeAdapterFactory) instance).create(gson, type); - } else if (instance instanceof JsonSerializer || instance instanceof JsonDeserializer) { - JsonSerializer serializer = instance instanceof JsonSerializer - ? (JsonSerializer) instance - : null; - JsonDeserializer deserializer = instance instanceof JsonDeserializer - ? (JsonDeserializer) instance - : null; - typeAdapter = new TreeTypeAdapter(serializer, deserializer, gson, type, null); - } else { - throw new IllegalArgumentException("Invalid attempt to bind an instance of " - + instance.getClass().getName() + " as a @JsonAdapter for " + type.toString() - + ". @JsonAdapter value must be a TypeAdapter, TypeAdapterFactory," - + " JsonSerializer or JsonDeserializer."); - } - - if (typeAdapter != null && annotation.nullSafe()) { - typeAdapter = typeAdapter.nullSafe(); - } - - return typeAdapter; - } -} diff --git a/src/gson/main/java/com/google/gson/internal/bind/JsonTreeReader.java b/src/gson/main/java/com/google/gson/internal/bind/JsonTreeReader.java deleted file mode 100644 index 387b29e..0000000 --- a/src/gson/main/java/com/google/gson/internal/bind/JsonTreeReader.java +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal.bind; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonNull; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import java.io.IOException; -import java.io.Reader; -import java.util.Iterator; -import java.util.Map; - -/** - * This reader walks the elements of a JsonElement as if it was coming from a - * character stream. - * - * @author Jesse Wilson - */ -public final class JsonTreeReader extends JsonReader { - private static final Reader UNREADABLE_READER = new Reader() { - @Override public int read(char[] buffer, int offset, int count) throws IOException { - throw new AssertionError(); - } - @Override public void close() throws IOException { - throw new AssertionError(); - } - }; - private static final Object SENTINEL_CLOSED = new Object(); - - /* - * The nesting stack. Using a manual array rather than an ArrayList saves 20%. - */ - private Object[] stack = new Object[32]; - private int stackSize = 0; - - /* - * The path members. It corresponds directly to stack: At indices where the - * stack contains an object (EMPTY_OBJECT, DANGLING_NAME or NONEMPTY_OBJECT), - * pathNames contains the name at this scope. Where it contains an array - * (EMPTY_ARRAY, NONEMPTY_ARRAY) pathIndices contains the current index in - * that array. Otherwise the value is undefined, and we take advantage of that - * by incrementing pathIndices when doing so isn't useful. - */ - private String[] pathNames = new String[32]; - private int[] pathIndices = new int[32]; - - public JsonTreeReader(JsonElement element) { - super(UNREADABLE_READER); - push(element); - } - - @Override public void beginArray() throws IOException { - expect(JsonToken.BEGIN_ARRAY); - JsonArray array = (JsonArray) peekStack(); - push(array.iterator()); - pathIndices[stackSize - 1] = 0; - } - - @Override public void endArray() throws IOException { - expect(JsonToken.END_ARRAY); - popStack(); // empty iterator - popStack(); // array - if (stackSize > 0) { - pathIndices[stackSize - 1]++; - } - } - - @Override public void beginObject() throws IOException { - expect(JsonToken.BEGIN_OBJECT); - JsonObject object = (JsonObject) peekStack(); - push(object.entrySet().iterator()); - } - - @Override public void endObject() throws IOException { - expect(JsonToken.END_OBJECT); - popStack(); // empty iterator - popStack(); // object - if (stackSize > 0) { - pathIndices[stackSize - 1]++; - } - } - - @Override public boolean hasNext() throws IOException { - JsonToken token = peek(); - return token != JsonToken.END_OBJECT && token != JsonToken.END_ARRAY; - } - - @Override public JsonToken peek() throws IOException { - if (stackSize == 0) { - return JsonToken.END_DOCUMENT; - } - - Object o = peekStack(); - if (o instanceof Iterator) { - boolean isObject = stack[stackSize - 2] instanceof JsonObject; - Iterator iterator = (Iterator) o; - if (iterator.hasNext()) { - if (isObject) { - return JsonToken.NAME; - } else { - push(iterator.next()); - return peek(); - } - } else { - return isObject ? JsonToken.END_OBJECT : JsonToken.END_ARRAY; - } - } else if (o instanceof JsonObject) { - return JsonToken.BEGIN_OBJECT; - } else if (o instanceof JsonArray) { - return JsonToken.BEGIN_ARRAY; - } else if (o instanceof JsonPrimitive) { - JsonPrimitive primitive = (JsonPrimitive) o; - if (primitive.isString()) { - return JsonToken.STRING; - } else if (primitive.isBoolean()) { - return JsonToken.BOOLEAN; - } else if (primitive.isNumber()) { - return JsonToken.NUMBER; - } else { - throw new AssertionError(); - } - } else if (o instanceof JsonNull) { - return JsonToken.NULL; - } else if (o == SENTINEL_CLOSED) { - throw new IllegalStateException("JsonReader is closed"); - } else { - throw new AssertionError(); - } - } - - private Object peekStack() { - return stack[stackSize - 1]; - } - - private Object popStack() { - Object result = stack[--stackSize]; - stack[stackSize] = null; - return result; - } - - private void expect(JsonToken expected) throws IOException { - if (peek() != expected) { - throw new IllegalStateException( - "Expected " + expected + " but was " + peek() + locationString()); - } - } - - @Override public String nextName() throws IOException { - expect(JsonToken.NAME); - Iterator i = (Iterator) peekStack(); - Map.Entry entry = (Map.Entry) i.next(); - String result = (String) entry.getKey(); - pathNames[stackSize - 1] = result; - push(entry.getValue()); - return result; - } - - @Override public String nextString() throws IOException { - JsonToken token = peek(); - if (token != JsonToken.STRING && token != JsonToken.NUMBER) { - throw new IllegalStateException( - "Expected " + JsonToken.STRING + " but was " + token + locationString()); - } - String result = ((JsonPrimitive) popStack()).getAsString(); - if (stackSize > 0) { - pathIndices[stackSize - 1]++; - } - return result; - } - - @Override public boolean nextBoolean() throws IOException { - expect(JsonToken.BOOLEAN); - boolean result = ((JsonPrimitive) popStack()).getAsBoolean(); - if (stackSize > 0) { - pathIndices[stackSize - 1]++; - } - return result; - } - - @Override public void nextNull() throws IOException { - expect(JsonToken.NULL); - popStack(); - if (stackSize > 0) { - pathIndices[stackSize - 1]++; - } - } - - @Override public double nextDouble() throws IOException { - JsonToken token = peek(); - if (token != JsonToken.NUMBER && token != JsonToken.STRING) { - throw new IllegalStateException( - "Expected " + JsonToken.NUMBER + " but was " + token + locationString()); - } - double result = ((JsonPrimitive) peekStack()).getAsDouble(); - if (!isLenient() && (Double.isNaN(result) || Double.isInfinite(result))) { - throw new NumberFormatException("JSON forbids NaN and infinities: " + result); - } - popStack(); - if (stackSize > 0) { - pathIndices[stackSize - 1]++; - } - return result; - } - - @Override public long nextLong() throws IOException { - JsonToken token = peek(); - if (token != JsonToken.NUMBER && token != JsonToken.STRING) { - throw new IllegalStateException( - "Expected " + JsonToken.NUMBER + " but was " + token + locationString()); - } - long result = ((JsonPrimitive) peekStack()).getAsLong(); - popStack(); - if (stackSize > 0) { - pathIndices[stackSize - 1]++; - } - return result; - } - - @Override public int nextInt() throws IOException { - JsonToken token = peek(); - if (token != JsonToken.NUMBER && token != JsonToken.STRING) { - throw new IllegalStateException( - "Expected " + JsonToken.NUMBER + " but was " + token + locationString()); - } - int result = ((JsonPrimitive) peekStack()).getAsInt(); - popStack(); - if (stackSize > 0) { - pathIndices[stackSize - 1]++; - } - return result; - } - - @Override public void close() throws IOException { - stack = new Object[] { SENTINEL_CLOSED }; - stackSize = 1; - } - - @Override public void skipValue() throws IOException { - if (peek() == JsonToken.NAME) { - nextName(); - pathNames[stackSize - 2] = "null"; - } else { - popStack(); - if (stackSize > 0) { - pathNames[stackSize - 1] = "null"; - } - } - if (stackSize > 0) { - pathIndices[stackSize - 1]++; - } - } - - @Override public String toString() { - return getClass().getSimpleName(); - } - - public void promoteNameToValue() throws IOException { - expect(JsonToken.NAME); - Iterator i = (Iterator) peekStack(); - Map.Entry entry = (Map.Entry) i.next(); - push(entry.getValue()); - push(new JsonPrimitive((String) entry.getKey())); - } - - private void push(Object newTop) { - if (stackSize == stack.length) { - Object[] newStack = new Object[stackSize * 2]; - int[] newPathIndices = new int[stackSize * 2]; - String[] newPathNames = new String[stackSize * 2]; - System.arraycopy(stack, 0, newStack, 0, stackSize); - System.arraycopy(pathIndices, 0, newPathIndices, 0, stackSize); - System.arraycopy(pathNames, 0, newPathNames, 0, stackSize); - stack = newStack; - pathIndices = newPathIndices; - pathNames = newPathNames; - } - stack[stackSize++] = newTop; - } - - @Override public String getPath() { - StringBuilder result = new StringBuilder().append('$'); - for (int i = 0; i < stackSize; i++) { - if (stack[i] instanceof JsonArray) { - if (stack[++i] instanceof Iterator) { - result.append('[').append(pathIndices[i]).append(']'); - } - } else if (stack[i] instanceof JsonObject) { - if (stack[++i] instanceof Iterator) { - result.append('.'); - if (pathNames[i] != null) { - result.append(pathNames[i]); - } - } - } - } - return result.toString(); - } - - private String locationString() { - return " at path " + getPath(); - } -} diff --git a/src/gson/main/java/com/google/gson/internal/bind/JsonTreeWriter.java b/src/gson/main/java/com/google/gson/internal/bind/JsonTreeWriter.java deleted file mode 100644 index 51dc1f3..0000000 --- a/src/gson/main/java/com/google/gson/internal/bind/JsonTreeWriter.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal.bind; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonNull; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.io.Writer; -import java.util.ArrayList; -import java.util.List; - -/** - * This writer creates a JsonElement. - */ -public final class JsonTreeWriter extends JsonWriter { - private static final Writer UNWRITABLE_WRITER = new Writer() { - @Override public void write(char[] buffer, int offset, int counter) { - throw new AssertionError(); - } - @Override public void flush() throws IOException { - throw new AssertionError(); - } - @Override public void close() throws IOException { - throw new AssertionError(); - } - }; - /** Added to the top of the stack when this writer is closed to cause following ops to fail. */ - private static final JsonPrimitive SENTINEL_CLOSED = new JsonPrimitive("closed"); - - /** The JsonElements and JsonArrays under modification, outermost to innermost. */ - private final List stack = new ArrayList(); - - /** The name for the next JSON object value. If non-null, the top of the stack is a JsonObject. */ - private String pendingName; - - /** the JSON element constructed by this writer. */ - private JsonElement product = JsonNull.INSTANCE; // TODO: is this really what we want?; - - public JsonTreeWriter() { - super(UNWRITABLE_WRITER); - } - - /** - * Returns the top level object produced by this writer. - */ - public JsonElement get() { - if (!stack.isEmpty()) { - throw new IllegalStateException("Expected one JSON element but was " + stack); - } - return product; - } - - private JsonElement peek() { - return stack.get(stack.size() - 1); - } - - private void put(JsonElement value) { - if (pendingName != null) { - if (!value.isJsonNull() || getSerializeNulls()) { - JsonObject object = (JsonObject) peek(); - object.add(pendingName, value); - } - pendingName = null; - } else if (stack.isEmpty()) { - product = value; - } else { - JsonElement element = peek(); - if (element instanceof JsonArray) { - ((JsonArray) element).add(value); - } else { - throw new IllegalStateException(); - } - } - } - - @Override public JsonWriter beginArray() throws IOException { - JsonArray array = new JsonArray(); - put(array); - stack.add(array); - return this; - } - - @Override public JsonWriter endArray() throws IOException { - if (stack.isEmpty() || pendingName != null) { - throw new IllegalStateException(); - } - JsonElement element = peek(); - if (element instanceof JsonArray) { - stack.remove(stack.size() - 1); - return this; - } - throw new IllegalStateException(); - } - - @Override public JsonWriter beginObject() throws IOException { - JsonObject object = new JsonObject(); - put(object); - stack.add(object); - return this; - } - - @Override public JsonWriter endObject() throws IOException { - if (stack.isEmpty() || pendingName != null) { - throw new IllegalStateException(); - } - JsonElement element = peek(); - if (element instanceof JsonObject) { - stack.remove(stack.size() - 1); - return this; - } - throw new IllegalStateException(); - } - - @Override public JsonWriter name(String name) throws IOException { - if (stack.isEmpty() || pendingName != null) { - throw new IllegalStateException(); - } - JsonElement element = peek(); - if (element instanceof JsonObject) { - pendingName = name; - return this; - } - throw new IllegalStateException(); - } - - @Override public JsonWriter value(String value) throws IOException { - if (value == null) { - return nullValue(); - } - put(new JsonPrimitive(value)); - return this; - } - - @Override public JsonWriter nullValue() throws IOException { - put(JsonNull.INSTANCE); - return this; - } - - @Override public JsonWriter value(boolean value) throws IOException { - put(new JsonPrimitive(value)); - return this; - } - - @Override public JsonWriter value(Boolean value) throws IOException { - if (value == null) { - return nullValue(); - } - put(new JsonPrimitive(value)); - return this; - } - - @Override public JsonWriter value(double value) throws IOException { - if (!isLenient() && (Double.isNaN(value) || Double.isInfinite(value))) { - throw new IllegalArgumentException("JSON forbids NaN and infinities: " + value); - } - put(new JsonPrimitive(value)); - return this; - } - - @Override public JsonWriter value(long value) throws IOException { - put(new JsonPrimitive(value)); - return this; - } - - @Override public JsonWriter value(Number value) throws IOException { - if (value == null) { - return nullValue(); - } - - if (!isLenient()) { - double d = value.doubleValue(); - if (Double.isNaN(d) || Double.isInfinite(d)) { - throw new IllegalArgumentException("JSON forbids NaN and infinities: " + value); - } - } - - put(new JsonPrimitive(value)); - return this; - } - - @Override public void flush() throws IOException { - } - - @Override public void close() throws IOException { - if (!stack.isEmpty()) { - throw new IOException("Incomplete document"); - } - stack.add(SENTINEL_CLOSED); - } -} diff --git a/src/gson/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java b/src/gson/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java deleted file mode 100644 index 5a34a5d..0000000 --- a/src/gson/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal.bind; - -import com.google.gson.Gson; -import com.google.gson.JsonElement; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSyntaxException; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.internal.$Gson$Types; -import com.google.gson.internal.ConstructorConstructor; -import com.google.gson.internal.JsonReaderInternalAccess; -import com.google.gson.internal.ObjectConstructor; -import com.google.gson.internal.Streams; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - * Adapts maps to either JSON objects or JSON arrays. - * - *

Maps as JSON objects

- * For primitive keys or when complex map key serialization is not enabled, this - * converts Java {@link Map Maps} to JSON Objects. This requires that map keys - * can be serialized as strings; this is insufficient for some key types. For - * example, consider a map whose keys are points on a grid. The default JSON - * form encodes reasonably:
   {@code
- *   Map original = new LinkedHashMap();
- *   original.put(new Point(5, 6), "a");
- *   original.put(new Point(8, 8), "b");
- *   System.out.println(gson.toJson(original, type));
- * }
- * The above code prints this JSON object:
   {@code
- *   {
- *     "(5,6)": "a",
- *     "(8,8)": "b"
- *   }
- * }
- * But GSON is unable to deserialize this value because the JSON string name is - * just the {@link Object#toString() toString()} of the map key. Attempting to - * convert the above JSON to an object fails with a parse exception: - *
com.google.gson.JsonParseException: Expecting object found: "(5,6)"
- *   at com.google.gson.JsonObjectDeserializationVisitor.visitFieldUsingCustomHandler
- *   at com.google.gson.ObjectNavigator.navigateClassFields
- *   ...
- * - *

Maps as JSON arrays

- * An alternative approach taken by this type adapter when it is required and - * complex map key serialization is enabled is to encode maps as arrays of map - * entries. Each map entry is a two element array containing a key and a value. - * This approach is more flexible because any type can be used as the map's key; - * not just strings. But it's also less portable because the receiver of such - * JSON must be aware of the map entry convention. - * - *

Register this adapter when you are creating your GSON instance. - *

   {@code
- *   Gson gson = new GsonBuilder()
- *     .registerTypeAdapter(Map.class, new MapAsArrayTypeAdapter())
- *     .create();
- * }
- * This will change the structure of the JSON emitted by the code above. Now we - * get an array. In this case the arrays elements are map entries: - *
   {@code
- *   [
- *     [
- *       {
- *         "x": 5,
- *         "y": 6
- *       },
- *       "a",
- *     ],
- *     [
- *       {
- *         "x": 8,
- *         "y": 8
- *       },
- *       "b"
- *     ]
- *   ]
- * }
- * This format will serialize and deserialize just fine as long as this adapter - * is registered. - */ -public final class MapTypeAdapterFactory implements TypeAdapterFactory { - private final ConstructorConstructor constructorConstructor; - final boolean complexMapKeySerialization; - - public MapTypeAdapterFactory(ConstructorConstructor constructorConstructor, - boolean complexMapKeySerialization) { - this.constructorConstructor = constructorConstructor; - this.complexMapKeySerialization = complexMapKeySerialization; - } - - @Override public TypeAdapter create(Gson gson, TypeToken typeToken) { - Type type = typeToken.getType(); - - Class rawType = typeToken.getRawType(); - if (!Map.class.isAssignableFrom(rawType)) { - return null; - } - - Class rawTypeOfSrc = $Gson$Types.getRawType(type); - Type[] keyAndValueTypes = $Gson$Types.getMapKeyAndValueTypes(type, rawTypeOfSrc); - TypeAdapter keyAdapter = getKeyAdapter(gson, keyAndValueTypes[0]); - TypeAdapter valueAdapter = gson.getAdapter(TypeToken.get(keyAndValueTypes[1])); - ObjectConstructor constructor = constructorConstructor.get(typeToken); - - @SuppressWarnings({"unchecked", "rawtypes"}) - // we don't define a type parameter for the key or value types - TypeAdapter result = new Adapter(gson, keyAndValueTypes[0], keyAdapter, - keyAndValueTypes[1], valueAdapter, constructor); - return result; - } - - /** - * Returns a type adapter that writes the value as a string. - */ - private TypeAdapter getKeyAdapter(Gson context, Type keyType) { - return (keyType == boolean.class || keyType == Boolean.class) - ? TypeAdapters.BOOLEAN_AS_STRING - : context.getAdapter(TypeToken.get(keyType)); - } - - private final class Adapter extends TypeAdapter> { - private final TypeAdapter keyTypeAdapter; - private final TypeAdapter valueTypeAdapter; - private final ObjectConstructor> constructor; - - public Adapter(Gson context, Type keyType, TypeAdapter keyTypeAdapter, - Type valueType, TypeAdapter valueTypeAdapter, - ObjectConstructor> constructor) { - this.keyTypeAdapter = - new TypeAdapterRuntimeTypeWrapper(context, keyTypeAdapter, keyType); - this.valueTypeAdapter = - new TypeAdapterRuntimeTypeWrapper(context, valueTypeAdapter, valueType); - this.constructor = constructor; - } - - @Override public Map read(JsonReader in) throws IOException { - JsonToken peek = in.peek(); - if (peek == JsonToken.NULL) { - in.nextNull(); - return null; - } - - Map map = constructor.construct(); - - if (peek == JsonToken.BEGIN_ARRAY) { - in.beginArray(); - while (in.hasNext()) { - in.beginArray(); // entry array - K key = keyTypeAdapter.read(in); - V value = valueTypeAdapter.read(in); - V replaced = map.put(key, value); - if (replaced != null) { - throw new JsonSyntaxException("duplicate key: " + key); - } - in.endArray(); - } - in.endArray(); - } else { - in.beginObject(); - while (in.hasNext()) { - JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in); - K key = keyTypeAdapter.read(in); - V value = valueTypeAdapter.read(in); - V replaced = map.put(key, value); - if (replaced != null) { - throw new JsonSyntaxException("duplicate key: " + key); - } - } - in.endObject(); - } - return map; - } - - @Override public void write(JsonWriter out, Map map) throws IOException { - if (map == null) { - out.nullValue(); - return; - } - - if (!complexMapKeySerialization) { - out.beginObject(); - for (Map.Entry entry : map.entrySet()) { - out.name(String.valueOf(entry.getKey())); - valueTypeAdapter.write(out, entry.getValue()); - } - out.endObject(); - return; - } - - boolean hasComplexKeys = false; - List keys = new ArrayList(map.size()); - - List values = new ArrayList(map.size()); - for (Map.Entry entry : map.entrySet()) { - JsonElement keyElement = keyTypeAdapter.toJsonTree(entry.getKey()); - keys.add(keyElement); - values.add(entry.getValue()); - hasComplexKeys |= keyElement.isJsonArray() || keyElement.isJsonObject(); - } - - if (hasComplexKeys) { - out.beginArray(); - for (int i = 0, size = keys.size(); i < size; i++) { - out.beginArray(); // entry array - Streams.write(keys.get(i), out); - valueTypeAdapter.write(out, values.get(i)); - out.endArray(); - } - out.endArray(); - } else { - out.beginObject(); - for (int i = 0, size = keys.size(); i < size; i++) { - JsonElement keyElement = keys.get(i); - out.name(keyToString(keyElement)); - valueTypeAdapter.write(out, values.get(i)); - } - out.endObject(); - } - } - - private String keyToString(JsonElement keyElement) { - if (keyElement.isJsonPrimitive()) { - JsonPrimitive primitive = keyElement.getAsJsonPrimitive(); - if (primitive.isNumber()) { - return String.valueOf(primitive.getAsNumber()); - } else if (primitive.isBoolean()) { - return Boolean.toString(primitive.getAsBoolean()); - } else if (primitive.isString()) { - return primitive.getAsString(); - } else { - throw new AssertionError(); - } - } else if (keyElement.isJsonNull()) { - return "null"; - } else { - throw new AssertionError(); - } - } - } -} diff --git a/src/gson/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java b/src/gson/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java deleted file mode 100644 index ec42e04..0000000 --- a/src/gson/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal.bind; - -import com.google.gson.Gson; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.internal.LinkedTreeMap; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - * Adapts types whose static type is only 'Object'. Uses getClass() on - * serialization and a primitive/Map/List on deserialization. - */ -public final class ObjectTypeAdapter extends TypeAdapter { - public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { - @SuppressWarnings("unchecked") - @Override public TypeAdapter create(Gson gson, TypeToken type) { - if (type.getRawType() == Object.class) { - return (TypeAdapter) new ObjectTypeAdapter(gson); - } - return null; - } - }; - - private final Gson gson; - - ObjectTypeAdapter(Gson gson) { - this.gson = gson; - } - - @Override public Object read(JsonReader in) throws IOException { - JsonToken token = in.peek(); - switch (token) { - case BEGIN_ARRAY: - List list = new ArrayList(); - in.beginArray(); - while (in.hasNext()) { - list.add(read(in)); - } - in.endArray(); - return list; - - case BEGIN_OBJECT: - Map map = new LinkedTreeMap(); - in.beginObject(); - while (in.hasNext()) { - map.put(in.nextName(), read(in)); - } - in.endObject(); - return map; - - case STRING: - return in.nextString(); - - case NUMBER: - return in.nextDouble(); - - case BOOLEAN: - return in.nextBoolean(); - - case NULL: - in.nextNull(); - return null; - - default: - throw new IllegalStateException(); - } - } - - @SuppressWarnings("unchecked") - @Override public void write(JsonWriter out, Object value) throws IOException { - if (value == null) { - out.nullValue(); - return; - } - - TypeAdapter typeAdapter = (TypeAdapter) gson.getAdapter(value.getClass()); - if (typeAdapter instanceof ObjectTypeAdapter) { - out.beginObject(); - out.endObject(); - return; - } - - typeAdapter.write(out, value); - } -} diff --git a/src/gson/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java b/src/gson/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java deleted file mode 100644 index 42798d0..0000000 --- a/src/gson/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal.bind; - -import com.google.gson.FieldNamingStrategy; -import com.google.gson.Gson; -import com.google.gson.JsonSyntaxException; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.google.gson.internal.$Gson$Types; -import com.google.gson.internal.ConstructorConstructor; -import com.google.gson.internal.Excluder; -import com.google.gson.internal.ObjectConstructor; -import com.google.gson.internal.Primitives; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -/** - * Type adapter that reflects over the fields and methods of a class. - */ -public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory { - private final ConstructorConstructor constructorConstructor; - private final FieldNamingStrategy fieldNamingPolicy; - private final Excluder excluder; - private final JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory; - - public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor, - FieldNamingStrategy fieldNamingPolicy, Excluder excluder, - JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory) { - this.constructorConstructor = constructorConstructor; - this.fieldNamingPolicy = fieldNamingPolicy; - this.excluder = excluder; - this.jsonAdapterFactory = jsonAdapterFactory; - } - - public boolean excludeField(Field f, boolean serialize) { - return excludeField(f, serialize, excluder); - } - - static boolean excludeField(Field f, boolean serialize, Excluder excluder) { - return !excluder.excludeClass(f.getType(), serialize) && !excluder.excludeField(f, serialize); - } - - /** first element holds the default name */ - private List getFieldNames(Field f) { - SerializedName annotation = f.getAnnotation(SerializedName.class); - if (annotation == null) { - String name = fieldNamingPolicy.translateName(f); - return Collections.singletonList(name); - } - - String serializedName = annotation.value(); - String[] alternates = annotation.alternate(); - if (alternates.length == 0) { - return Collections.singletonList(serializedName); - } - - List fieldNames = new ArrayList(alternates.length + 1); - fieldNames.add(serializedName); - for (String alternate : alternates) { - fieldNames.add(alternate); - } - return fieldNames; - } - - @Override public TypeAdapter create(Gson gson, final TypeToken type) { - Class raw = type.getRawType(); - - if (!Object.class.isAssignableFrom(raw)) { - return null; // it's a primitive! - } - - ObjectConstructor constructor = constructorConstructor.get(type); - return new Adapter(constructor, getBoundFields(gson, type, raw)); - } - - private ReflectiveTypeAdapterFactory.BoundField createBoundField( - final Gson context, final Field field, final String name, - final TypeToken fieldType, boolean serialize, boolean deserialize) { - final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType()); - // special casing primitives here saves ~5% on Android... - JsonAdapter annotation = field.getAnnotation(JsonAdapter.class); - TypeAdapter mapped = null; - if (annotation != null) { - mapped = jsonAdapterFactory.getTypeAdapter( - constructorConstructor, context, fieldType, annotation); - } - final boolean jsonAdapterPresent = mapped != null; - if (mapped == null) mapped = context.getAdapter(fieldType); - - final TypeAdapter typeAdapter = mapped; - return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) { - @SuppressWarnings({"unchecked", "rawtypes"}) // the type adapter and field type always agree - @Override void write(JsonWriter writer, Object value) - throws IOException, IllegalAccessException { - Object fieldValue = field.get(value); - TypeAdapter t = jsonAdapterPresent ? typeAdapter - : new TypeAdapterRuntimeTypeWrapper(context, typeAdapter, fieldType.getType()); - t.write(writer, fieldValue); - } - @Override void read(JsonReader reader, Object value) - throws IOException, IllegalAccessException { - Object fieldValue = typeAdapter.read(reader); - if (fieldValue != null || !isPrimitive) { - field.set(value, fieldValue); - } - } - @Override public boolean writeField(Object value) throws IOException, IllegalAccessException { - if (!serialized) return false; - Object fieldValue = field.get(value); - return fieldValue != value; // avoid recursion for example for Throwable.cause - } - }; - } - - private Map getBoundFields(Gson context, TypeToken type, Class raw) { - Map result = new LinkedHashMap(); - if (raw.isInterface()) { - return result; - } - - Type declaredType = type.getType(); - while (raw != Object.class) { - Field[] fields = raw.getDeclaredFields(); - for (Field field : fields) { - boolean serialize = excludeField(field, true); - boolean deserialize = excludeField(field, false); - if (!serialize && !deserialize) { - continue; - } - field.setAccessible(true); - Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType()); - List fieldNames = getFieldNames(field); - BoundField previous = null; - for (int i = 0, size = fieldNames.size(); i < size; ++i) { - String name = fieldNames.get(i); - if (i != 0) serialize = false; // only serialize the default name - BoundField boundField = createBoundField(context, field, name, - TypeToken.get(fieldType), serialize, deserialize); - BoundField replaced = result.put(name, boundField); - if (previous == null) previous = replaced; - } - if (previous != null) { - throw new IllegalArgumentException(declaredType - + " declares multiple JSON fields named " + previous.name); - } - } - type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass())); - raw = type.getRawType(); - } - return result; - } - - static abstract class BoundField { - final String name; - final boolean serialized; - final boolean deserialized; - - protected BoundField(String name, boolean serialized, boolean deserialized) { - this.name = name; - this.serialized = serialized; - this.deserialized = deserialized; - } - abstract boolean writeField(Object value) throws IOException, IllegalAccessException; - abstract void write(JsonWriter writer, Object value) throws IOException, IllegalAccessException; - abstract void read(JsonReader reader, Object value) throws IOException, IllegalAccessException; - } - - public static final class Adapter extends TypeAdapter { - private final ObjectConstructor constructor; - private final Map boundFields; - - Adapter(ObjectConstructor constructor, Map boundFields) { - this.constructor = constructor; - this.boundFields = boundFields; - } - - @Override public T read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - - T instance = constructor.construct(); - - try { - in.beginObject(); - while (in.hasNext()) { - String name = in.nextName(); - BoundField field = boundFields.get(name); - if (field == null || !field.deserialized) { - in.skipValue(); - } else { - field.read(in, instance); - } - } - } catch (IllegalStateException e) { - throw new JsonSyntaxException(e); - } catch (IllegalAccessException e) { - throw new AssertionError(e); - } - in.endObject(); - return instance; - } - - @Override public void write(JsonWriter out, T value) throws IOException { - if (value == null) { - out.nullValue(); - return; - } - - out.beginObject(); - try { - for (BoundField boundField : boundFields.values()) { - if (boundField.writeField(value)) { - out.name(boundField.name); - boundField.write(out, value); - } - } - } catch (IllegalAccessException e) { - throw new AssertionError(e); - } - out.endObject(); - } - } -} diff --git a/src/gson/main/java/com/google/gson/internal/bind/SqlDateTypeAdapter.java b/src/gson/main/java/com/google/gson/internal/bind/SqlDateTypeAdapter.java deleted file mode 100644 index 5ec244f..0000000 --- a/src/gson/main/java/com/google/gson/internal/bind/SqlDateTypeAdapter.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal.bind; - -import com.google.gson.Gson; -import com.google.gson.JsonSyntaxException; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; - -/** - * Adapter for java.sql.Date. Although this class appears stateless, it is not. - * DateFormat captures its time zone and locale when it is created, which gives - * this class state. DateFormat isn't thread safe either, so this class has - * to synchronize its read and write methods. - */ -public final class SqlDateTypeAdapter extends TypeAdapter { - public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { - @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal - @Override public TypeAdapter create(Gson gson, TypeToken typeToken) { - return typeToken.getRawType() == java.sql.Date.class - ? (TypeAdapter) new SqlDateTypeAdapter() : null; - } - }; - - private final DateFormat format = new SimpleDateFormat("MMM d, yyyy"); - - @Override - public synchronized java.sql.Date read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - try { - final long utilDate = format.parse(in.nextString()).getTime(); - return new java.sql.Date(utilDate); - } catch (ParseException e) { - throw new JsonSyntaxException(e); - } - } - - @Override - public synchronized void write(JsonWriter out, java.sql.Date value) throws IOException { - out.value(value == null ? null : format.format(value)); - } -} diff --git a/src/gson/main/java/com/google/gson/internal/bind/TimeTypeAdapter.java b/src/gson/main/java/com/google/gson/internal/bind/TimeTypeAdapter.java deleted file mode 100644 index 55d4b2f..0000000 --- a/src/gson/main/java/com/google/gson/internal/bind/TimeTypeAdapter.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gson.internal.bind; - -import com.google.gson.Gson; -import com.google.gson.JsonSyntaxException; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.sql.Time; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; - -/** - * Adapter for Time. Although this class appears stateless, it is not. - * DateFormat captures its time zone and locale when it is created, which gives - * this class state. DateFormat isn't thread safe either, so this class has - * to synchronize its read and write methods. - */ -public final class TimeTypeAdapter extends TypeAdapter