├── .classpath
├── .gitignore
├── .project
├── .settings
├── org.eclipse.core.resources.prefs
├── org.eclipse.jdt.core.prefs
└── org.eclipse.jdt.ui.prefs
├── LICENSE
├── README.md
├── pom.xml
└── src
└── com
└── esotericsoftware
└── jsonbeans
├── Json.java
├── JsonException.java
├── JsonReader.java
├── JsonSerializable.java
├── JsonSerializer.java
├── JsonValue.java
├── JsonWriter.java
├── Null.java
├── ObjectMap.java
├── OrderedMap.java
├── OutputType.java
└── ReadOnlySerializer.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Temporary Files
2 | *~
3 | .*.swp
4 | .DS_STORE
5 |
6 | bin/
7 | target/
8 | .idea/
9 | *.iml
10 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | jsonbeans
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.jdt.core.javanature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | encoding//src/com/esotericsoftware/jsonbeans/JsonReader.java=UTF-8
3 | encoding/=Cp1252
4 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=enabled
3 | org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
4 | org.eclipse.jdt.core.compiler.annotation.nonnull=com.esotericsoftware.jsonbeans.Null.NonNull
5 | org.eclipse.jdt.core.compiler.annotation.nonnull.secondary=
6 | org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=com.esotericsoftware.jsonbeans.Null.NonNullByDefault
7 | org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary=
8 | org.eclipse.jdt.core.compiler.annotation.nullable=com.esotericsoftware.jsonbeans.Null
9 | org.eclipse.jdt.core.compiler.annotation.nullable.secondary=com.esotericsoftware.spine.launcher.utils.Null,com.esotericsoftware.spine.server.utils.Null,com.esotericsoftware.spine.editor.utils.Null
10 | org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled
11 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
12 | org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
13 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
14 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
15 | org.eclipse.jdt.core.compiler.compliance=1.8
16 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate
17 | org.eclipse.jdt.core.compiler.debug.localVariable=generate
18 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate
19 | org.eclipse.jdt.core.compiler.doc.comment.support=enabled
20 | org.eclipse.jdt.core.compiler.problem.APILeak=warning
21 | org.eclipse.jdt.core.compiler.problem.annotatedTypeArgumentToUnannotated=info
22 | org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
23 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
24 | org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
25 | org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
26 | org.eclipse.jdt.core.compiler.problem.deadCode=ignore
27 | org.eclipse.jdt.core.compiler.problem.deprecation=ignore
28 | org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
29 | org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
30 | org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
31 | org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
32 | org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
33 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
34 | org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
35 | org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
36 | org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
37 | org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
38 | org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
39 | org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
40 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
41 | org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
42 | org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled
43 | org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
44 | org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
45 | org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
46 | org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
47 | org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
48 | org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
49 | org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
50 | org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
51 | org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
52 | org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
53 | org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
54 | org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
55 | org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
56 | org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
57 | org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
58 | org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=enabled
59 | org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public
60 | org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
61 | org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
62 | org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=enabled
63 | org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
64 | org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
65 | org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
66 | org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore
67 | org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
68 | org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
69 | org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
70 | org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
71 | org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
72 | org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning
73 | org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=info
74 | org.eclipse.jdt.core.compiler.problem.nullReference=warning
75 | org.eclipse.jdt.core.compiler.problem.nullSpecViolation=warning
76 | org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
77 | org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
78 | org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
79 | org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning
80 | org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
81 | org.eclipse.jdt.core.compiler.problem.potentialNullReference=info
82 | org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
83 | org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
84 | org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
85 | org.eclipse.jdt.core.compiler.problem.redundantNullCheck=info
86 | org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
87 | org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
88 | org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
89 | org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
90 | org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
91 | org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
92 | org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
93 | org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
94 | org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
95 | org.eclipse.jdt.core.compiler.problem.suppressWarningsNotFullyAnalysed=ignore
96 | org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=enabled
97 | org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning
98 | org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning
99 | org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
100 | org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
101 | org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=ignore
102 | org.eclipse.jdt.core.compiler.problem.unclosedCloseable=ignore
103 | org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
104 | org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
105 | org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning
106 | org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled
107 | org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info
108 | org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
109 | org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
110 | org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
111 | org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName=warning
112 | org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
113 | org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
114 | org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
115 | org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled
116 | org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore
117 | org.eclipse.jdt.core.compiler.problem.unusedImport=ignore
118 | org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
119 | org.eclipse.jdt.core.compiler.problem.unusedLocal=ignore
120 | org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore
121 | org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
122 | org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
123 | org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=enabled
124 | org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=enabled
125 | org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=ignore
126 | org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
127 | org.eclipse.jdt.core.compiler.problem.unusedWarningToken=ignore
128 | org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
129 | org.eclipse.jdt.core.compiler.release=enabled
130 | org.eclipse.jdt.core.compiler.source=1.8
131 | org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
132 | org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16
133 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
134 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
135 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
136 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
137 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
138 | org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
139 | org.eclipse.jdt.core.formatter.alignment_for_assignment=0
140 | org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16
141 | org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
142 | org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
143 | org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
144 | org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
145 | org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16
146 | org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
147 | org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
148 | org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16
149 | org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
150 | org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
151 | org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
152 | org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
153 | org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16
154 | org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
155 | org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
156 | org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
157 | org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
158 | org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
159 | org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
160 | org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
161 | org.eclipse.jdt.core.formatter.blank_lines_after_package=1
162 | org.eclipse.jdt.core.formatter.blank_lines_before_field=0
163 | org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
164 | org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
165 | org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
166 | org.eclipse.jdt.core.formatter.blank_lines_before_method=1
167 | org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
168 | org.eclipse.jdt.core.formatter.blank_lines_before_package=1
169 | org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
170 | org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
171 | org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
172 | org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
173 | org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
174 | org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
175 | org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
176 | org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
177 | org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
178 | org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
179 | org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
180 | org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
181 | org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
182 | org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
183 | org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
184 | org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
185 | org.eclipse.jdt.core.formatter.comment.format_block_comments=true
186 | org.eclipse.jdt.core.formatter.comment.format_header=false
187 | org.eclipse.jdt.core.formatter.comment.format_html=true
188 | org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
189 | org.eclipse.jdt.core.formatter.comment.format_line_comments=true
190 | org.eclipse.jdt.core.formatter.comment.format_source_code=true
191 | org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
192 | org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
193 | org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=do not insert
194 | org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
195 | org.eclipse.jdt.core.formatter.comment.line_length=130
196 | org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
197 | org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=false
198 | org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
199 | org.eclipse.jdt.core.formatter.compact_else_if=true
200 | org.eclipse.jdt.core.formatter.continuation_indentation=1
201 | org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=1
202 | org.eclipse.jdt.core.formatter.disabling_tag=@off
203 | org.eclipse.jdt.core.formatter.enabling_tag=@on
204 | org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
205 | org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
206 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
207 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
208 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
209 | org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
210 | org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
211 | org.eclipse.jdt.core.formatter.indent_empty_lines=false
212 | org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
213 | org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
214 | org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
215 | org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
216 | org.eclipse.jdt.core.formatter.indentation.size=4
217 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=do not insert
218 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
219 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
220 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
221 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
222 | org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
223 | org.eclipse.jdt.core.formatter.insert_new_line_after_label=insert
224 | org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
225 | org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
226 | org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
227 | org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
228 | org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
229 | org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
230 | org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
231 | org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
232 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
233 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
234 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
235 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
236 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
237 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
238 | org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
239 | org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert
240 | org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
241 | org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
242 | org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
243 | org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
244 | org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert
245 | org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
246 | org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
247 | org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
248 | org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=do not insert
249 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
250 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
251 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
252 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
253 | org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
254 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
255 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
256 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
257 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
258 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
259 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
260 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
261 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
262 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
263 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
264 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
265 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
266 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
267 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
268 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
269 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
270 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
271 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
272 | org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
273 | org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
274 | org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
275 | org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert
276 | org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert
277 | org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
278 | org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
279 | org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
280 | org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
281 | org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
282 | org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
283 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
284 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
285 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
286 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
287 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
288 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
289 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
290 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
291 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
292 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
293 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
294 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
295 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
296 | org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
297 | org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
298 | org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
299 | org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
300 | org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
301 | org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert
302 | org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
303 | org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
304 | org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert
305 | org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert
306 | org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
307 | org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert
308 | org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
309 | org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
310 | org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
311 | org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert
312 | org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
313 | org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
314 | org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
315 | org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
316 | org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
317 | org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
318 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
319 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
320 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
321 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
322 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
323 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
324 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
325 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
326 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
327 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
328 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
329 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
330 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
331 | org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
332 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
333 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
334 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
335 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
336 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
337 | org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
338 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
339 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
340 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
341 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
342 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
343 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
344 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
345 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
346 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
347 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
348 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
349 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
350 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
351 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
352 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
353 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
354 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
355 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
356 | org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
357 | org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
358 | org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
359 | org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert
360 | org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert
361 | org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
362 | org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
363 | org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
364 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
365 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
366 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
367 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
368 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
369 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
370 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
371 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
372 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
373 | org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
374 | org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
375 | org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
376 | org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
377 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
378 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
379 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
380 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=insert
381 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
382 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
383 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
384 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=insert
385 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
386 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
387 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
388 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
389 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
390 | org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
391 | org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
392 | org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
393 | org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
394 | org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
395 | org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
396 | org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
397 | org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert
398 | org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
399 | org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
400 | org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
401 | org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert
402 | org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert
403 | org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
404 | org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
405 | org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
406 | org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
407 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
408 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
409 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
410 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
411 | org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
412 | org.eclipse.jdt.core.formatter.join_lines_in_comments=true
413 | org.eclipse.jdt.core.formatter.join_wrapped_lines=true
414 | org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
415 | org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
416 | org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
417 | org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
418 | org.eclipse.jdt.core.formatter.lineSplit=130
419 | org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=true
420 | org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=true
421 | org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
422 | org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
423 | org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
424 | org.eclipse.jdt.core.formatter.tabulation.char=tab
425 | org.eclipse.jdt.core.formatter.tabulation.size=3
426 | org.eclipse.jdt.core.formatter.use_on_off_tags=true
427 | org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
428 | org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true
429 | org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true
430 | org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true
431 | org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true
432 | org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
433 | org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true
434 | org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
435 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.ui.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | formatter_profile=_libgdx
3 | formatter_settings_version=12
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2010, Nathan Sweet
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5 |
6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8 | * Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
9 |
10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## JsonBeans
2 |
3 | Please use the [JsonBeans discussion group](http://groups.google.com/group/jsonbeans-users) for support.
4 |
5 | - [Overview](#Overview)
6 | - [Writing object graphs](#writing-object-graphs)
7 | - [Reading object graphs](#reading-object-graphs)
8 | - [Customizing serialization](#customizing-serialization)
9 | - [Event based parsing](#event-based-parsing)
10 |
11 | ## Overview
12 |
13 | JsonBeans is a lightweight library that makes it easy to serialize and deserialize Java object graphs to and from JSON. The JAR is 45k and has no dependencies. Four small classes make up the important parts of the library:
14 |
15 | - `JsonWriter`: A builder style API for emitting JSON.
16 | - `JsonReader`: Parses JSON and builds a DOM of `JsonValue` objects.
17 | - `JsonValue`: Describes a JSON object, array, string, float, long, boolean, or null.
18 | - `Json`: Reads and writes arbitrary object graphs using `JsonReader` and `JsonWriter`.
19 |
20 | ## Writing object graphs
21 |
22 | The `Json` class uses reflection to automatically serialize objects to JSON. For example, here are two classes (getters/setters and constructors omitted):
23 |
24 | ```java
25 | public class Person {
26 | private String name;
27 | private int age;
28 | private ArrayList numbers;
29 | }
30 |
31 | public class PhoneNumber {
32 | private String name;
33 | private String number;
34 | }
35 | ```
36 |
37 | Example object graph using these classes:
38 |
39 | ```java
40 | Person person = new Person();
41 | person.setName("Nate");
42 | person.setAge(31);
43 | ArrayList numbers = new ArrayList();
44 | numbers.add(new PhoneNumber("Home", "206-555-1234"));
45 | numbers.add(new PhoneNumber("Work", "425-555-4321"));
46 | person.setNumbers(numbers);
47 | ```
48 |
49 | The JsonBeans code to serialize this object graph:
50 |
51 | ```java
52 | Json json = new Json();
53 | System.out.println(json.toJson(person));
54 | ```
55 | ```json
56 | {"numbers":[{"class":"com.example.PhoneNumber","name":"Home","number":"206-555-1234"},{"class":"com.example.PhoneNumber","name":"Work","number":"425-555-4321"}],"age":31,"name":"Nate"}
57 | ```
58 |
59 | That is compact, but hardly legible. The `prettyPrint` method can be used:
60 |
61 | ```java
62 | Json json = new Json();
63 | System.out.println(json.prettyPrint(person));
64 | ```
65 | ```json
66 | {
67 | "name": "Nate",
68 | "age": 31,
69 | "numbers": [
70 | {
71 | "name": "Home",
72 | "class": "com.example.PhoneNumber",
73 | "number": "206-555-1234"
74 | },
75 | {
76 | "name": "Work",
77 | "class": "com.example.PhoneNumber",
78 | "number": "425-555-4321"
79 | }
80 | ]
81 | }
82 | ```
83 |
84 | Note that the class for the `PhoneNumber` objects in the `ArrayList numbers` field appears in the JSON. This is required to recreate the object graph from the JSON because `ArrayList` can hold any type of object. Class names are only output when they are required for deserialization. If the field was `ArrayList numbers` then class names would only appear when an item in the list extends `PhoneNumber`. If you know the concrete type or aren't using generics, you can avoid class names being written by telling the `Json` class the types:
85 |
86 | ```java
87 | Json json = new Json();
88 | json.setElementType(Person.class, "numbers", PhoneNumber.class);
89 | System.out.println(json.prettyPrint(person));
90 | ```
91 | ```json
92 | {
93 | "name": "Nate",
94 | "age": 31,
95 | "numbers": [
96 | {
97 | "name": "Home",
98 | "number": "206-555-1234"
99 | },
100 | {
101 | "name": "Work",
102 | "number": "425-555-4321"
103 | }
104 | ]
105 | }
106 | ```
107 |
108 | When writing the class cannot be avoided, an alias can be given:
109 |
110 | ```java
111 | Json json = new Json();
112 | json.addClassTag("phoneNumber", PhoneNumber.class);
113 | System.out.println(json.prettyPrint(person));
114 | ```
115 | ```json
116 | {
117 | "name": "Nate",
118 | "age": 31,
119 | "numbers": [
120 | {
121 | "name": "Home",
122 | "class": "phoneNumber",
123 | "number": "206-555-1234"
124 | },
125 | {
126 | "name": "Work",
127 | "class": "phoneNumber",
128 | "number": "425-555-4321"
129 | }
130 | ]
131 | }
132 | ```
133 |
134 | JsonBeans can write and read both JSON and a couple JSON-like formats. It supports "javascript", where the object property names are only quoted when needed. It also supports a "minimal" format, where both object property names and values are only quoted when needed.
135 |
136 | ```java
137 | Json json = new Json();
138 | json.setOutputType(OutputType.minimal);
139 | json.setElementType(Person.class, "numbers", PhoneNumber.class);
140 | System.out.println(json.prettyPrint(person));
141 | ```
142 | ```json
143 | {
144 | name: Nate,
145 | age: 31,
146 | numbers: [
147 | {
148 | name: Home,
149 | number: "206-555-1234"
150 | },
151 | {
152 | name: Work,
153 | number: "425-555-4321"
154 | }
155 | ]
156 | }
157 | ```
158 |
159 | ## Reading object graphs
160 |
161 | The Json class uses reflection to automatically deserialize objects from JSON. Here is how to deserialize the JSON from the previous examples:
162 |
163 | ```java
164 | Json json = new Json();
165 | String text = json.toJson(person);
166 | Person person2 = json.fromJson(Person.class, text);
167 | ```
168 |
169 | The type passed to `fromJson` is the type of the root of the object graph. From this, JsonBeans can determine the types of all the fields and all other objects encountered, recursively. The "knownType" and "elementType" of the root can be passed to `toJson`. This is useful if the type of the root object is not known:
170 |
171 | ```java
172 | Json json = new Json();
173 | json.setOutputType(OutputType.minimal);
174 | String text = json.toJson(person, Object.class);
175 | System.out.println(json.prettyPrint(text));
176 | Object person2 = json.fromJson(Object.class, text);
177 | ```
178 | ```json
179 | {
180 | class: com.example.Person,
181 | name: Nate,
182 | age: 31,
183 | numbers: [
184 | {
185 | name: Home,
186 | class: com.example.PhoneNumber,
187 | number: "206-555-1234"
188 | },
189 | {
190 | name: Work,
191 | class: com.example.PhoneNumber,
192 | number: "425-555-4321"
193 | }
194 | ]
195 | }
196 | ```
197 |
198 | To read the JSON as a DOM of maps, arrays, and values, the `JsonReader` class can be used:
199 |
200 | ```java
201 | Json json = new Json();
202 | String text = json.toJson(person, Object.class);
203 | JsonValue root = new JsonReader().parse(text);
204 | ```
205 |
206 | The `JsonValue` describes a JSON object, array, string, float, long, boolean, or null.
207 |
208 | ## Customizing serialization
209 |
210 | Serialization can be customized by either having the class to be serialized implement the `Json.Serializable` interface, or by registering a `Json.Serializer` with the `Json` instance. This example writes the phone numbers as an object with a single field:
211 |
212 | ```java
213 | static public class PhoneNumber implements Json.Serializable {
214 | private String name;
215 | private String number;
216 |
217 | public void write (Json json) {
218 | json.writeValue(name, number);
219 | }
220 |
221 | public void read (Json json, JsonValue jsonMap) {
222 | name = jsonMap.child().name();
223 | number = jsonMap.child().asString();
224 | }
225 | }
226 |
227 | Json json = new Json();
228 | json.setElementType(Person.class, "numbers", PhoneNumber.class);
229 | String text = json.prettyPrint(person);
230 | System.out.println(text);
231 | Person person2 = json.fromJson(Person.class, text);
232 | ```
233 | ```json
234 | {
235 | "name": "Nate",
236 | "age": 31
237 | "numbers": [
238 | {
239 | "Home": "206-555-1234"
240 | },
241 | {
242 | "Work": "425-555-4321"
243 | }
244 | ]
245 | }
246 | ```
247 |
248 | In the `Json.Serializable` interface methods, the `Json` instance is given. It has many methods to read and write data to the JSON. When using `Json.Serializable`, the surrounding JSON object is handled automatically in the `write` method. This is why the `read` method always receives a `JsonMap`.
249 |
250 | `Json.Serializer` provides more control over what is output, requiring `writeObjectStart` and `writeObjectEnd` to be called to achieve the same effect. A JSON array or a simple value could be output instead of an object. `Json.Serializer` also allows the object creation to be customized.
251 |
252 | ```java
253 | Json json = new Json();
254 | json.setSerializer(PhoneNumber.class, new Json.Serializer() {
255 | public void write (Json json, PhoneNumber number, Class knownType) {
256 | json.writeObjectStart();
257 | json.writeValue(number.name, number.number);
258 | json.writeObjectEnd();
259 | }
260 |
261 | public PhoneNumber read (Json json, JsonValue jsonData, Class type) {
262 | PhoneNumber number = new PhoneNumber();
263 | number.setName(jsonData.child().name());
264 | number.setNumber(jsonData.child().asString());
265 | return number;
266 | }
267 | });
268 | json.setElementType(Person.class, "numbers", PhoneNumber.class);
269 | String text = json.prettyPrint(person);
270 | System.out.println(text);
271 | Person person2 = json.fromJson(Person.class, text);
272 | ```
273 |
274 | ## Event based parsing
275 |
276 | The `JsonReader` class reads JSON and has protected methods that are called as JSON objects, arrays, strings, numbers, and booleans are encountered. By default, these methods build a DOM out of `JsonValue` objects. These methods can be overridden to do your own event based JSON handling.
277 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
5 | 4.0.0
6 | com.esotericsoftware
7 | jsonbeans
8 | 1.0-SNAPSHOT
9 | jar
10 | JsonBeans
11 | Java object graphs, to and from JSON automatically
12 | https://github.com/EsotericSoftware/jsonbeans
13 |
14 |
15 |
16 | New BSD License
17 | http://www.opensource.org/licenses/bsd-license.php
18 | repo
19 |
20 |
21 |
22 |
23 | https://github.com/EsotericSoftware/jsonbeans
24 | scm:git:git@github.com:EsotericSoftware/jsonbeans.git
25 | scm:git:git@github.com:EsotericSoftware/jsonbeans.git
26 |
27 |
28 |
29 |
30 | nathan.sweet
31 | Nathan Sweet
32 | nathan.sweet@gmail.com
33 |
34 |
35 |
36 |
37 | UTF-8
38 |
39 |
40 |
41 |
42 | src
43 |
44 |
45 |
46 |
47 | maven-resources-plugin
48 | 2.5
49 |
50 |
51 | default-resources
52 | none
53 |
54 |
55 | default-testResources
56 | none
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/src/com/esotericsoftware/jsonbeans/Json.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011 See AUTHORS file.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | ******************************************************************************/
16 |
17 | package com.esotericsoftware.jsonbeans;
18 |
19 | import java.io.File;
20 | import java.io.FileOutputStream;
21 | import java.io.IOException;
22 | import java.io.InputStream;
23 | import java.io.OutputStreamWriter;
24 | import java.io.Reader;
25 | import java.io.StringWriter;
26 | import java.io.Writer;
27 | import java.lang.reflect.Array;
28 | import java.lang.reflect.Constructor;
29 | import java.lang.reflect.Field;
30 | import java.lang.reflect.GenericArrayType;
31 | import java.lang.reflect.Modifier;
32 | import java.lang.reflect.ParameterizedType;
33 | import java.lang.reflect.Type;
34 | import java.util.ArrayList;
35 | import java.util.Arrays;
36 | import java.util.Collection;
37 | import java.util.Collections;
38 | import java.util.HashMap;
39 | import java.util.Map;
40 |
41 | import com.esotericsoftware.jsonbeans.JsonValue.PrettyPrintSettings;
42 | import com.esotericsoftware.jsonbeans.ObjectMap.Entry;
43 |
44 | /** Reads/writes Java objects to/from JSON, automatically. See the wiki for usage:
45 | * https://github.com/libgdx/libgdx/wiki/Reading-and-writing-JSON
46 | * @author Nathan Sweet */
47 | public class Json {
48 | static private final boolean debug = false;
49 |
50 | private JsonWriter writer;
51 | private String typeName = "class";
52 | private boolean usePrototypes = true;
53 | private OutputType outputType;
54 | private boolean quoteLongValues;
55 | private boolean ignoreUnknownFields;
56 | private boolean ignoreDeprecated;
57 | private boolean readDeprecated;
58 | private boolean enumNames = true;
59 | private boolean sortFields;
60 | private JsonSerializer defaultSerializer;
61 | private final ObjectMap> typeToFields = new ObjectMap();
62 | private final ObjectMap tagToClass = new ObjectMap();
63 | private final ObjectMap classToTag = new ObjectMap();
64 | private final ObjectMap classToSerializer = new ObjectMap();
65 | private final ObjectMap classToDefaultValues = new ObjectMap();
66 | private final Object[] equals1 = {null}, equals2 = {null};
67 |
68 | public Json () {
69 | outputType = OutputType.minimal;
70 | }
71 |
72 | public Json (OutputType outputType) {
73 | this.outputType = outputType;
74 | }
75 |
76 | /** When true, fields in the JSON that are not found on the class will not throw a {@link JsonException}. Default is false. */
77 | public void setIgnoreUnknownFields (boolean ignoreUnknownFields) {
78 | this.ignoreUnknownFields = ignoreUnknownFields;
79 | }
80 |
81 | public boolean getIgnoreUnknownFields () {
82 | return ignoreUnknownFields;
83 | }
84 |
85 | /** When true, fields with the {@link Deprecated} annotation will not be read or written. Default is false.
86 | * @see #setReadDeprecated(boolean)
87 | * @see #setDeprecated(Class, String, boolean) */
88 | public void setIgnoreDeprecated (boolean ignoreDeprecated) {
89 | this.ignoreDeprecated = ignoreDeprecated;
90 | }
91 |
92 | /** When true, fields with the {@link Deprecated} annotation will be read (but not written) when
93 | * {@link #setIgnoreDeprecated(boolean)} is true. Default is false.
94 | * @see #setDeprecated(Class, String, boolean) */
95 | public void setReadDeprecated (boolean readDeprecated) {
96 | this.readDeprecated = readDeprecated;
97 | }
98 |
99 | /** Default is {@link OutputType#minimal}.
100 | * @see JsonWriter#setOutputType(OutputType) */
101 | public void setOutputType (OutputType outputType) {
102 | this.outputType = outputType;
103 | }
104 |
105 | /** Default is false.
106 | * @see JsonWriter#setQuoteLongValues(boolean) */
107 | public void setQuoteLongValues (boolean quoteLongValues) {
108 | this.quoteLongValues = quoteLongValues;
109 | }
110 |
111 | /** When true, {@link Enum#name()} is used to write enum values. When false, {@link Enum#toString()} is used which may not be
112 | * unique. Default is true. */
113 | public void setEnumNames (boolean enumNames) {
114 | this.enumNames = enumNames;
115 | }
116 |
117 | /** Sets a tag to use instead of the fully qualifier class name. This can make the JSON easier to read. */
118 | public void addClassTag (String tag, Class type) {
119 | tagToClass.put(tag, type);
120 | classToTag.put(type, tag);
121 | }
122 |
123 | /** Returns the class for the specified tag, or null. */
124 | public @Null Class getClass (String tag) {
125 | return tagToClass.get(tag);
126 | }
127 |
128 | /** Returns the tag for the specified class, or null. */
129 | public @Null String getTag (Class type) {
130 | return classToTag.get(type);
131 | }
132 |
133 | /** Sets the name of the JSON field to store the Java class name or class tag when required to avoid ambiguity during
134 | * deserialization. Set to null to never output this information, but be warned that deserialization may fail. Default is
135 | * "class". */
136 | public void setTypeName (@Null String typeName) {
137 | this.typeName = typeName;
138 | }
139 |
140 | /** Sets the serializer to use when the type being deserialized is not known (null). */
141 | public void setDefaultSerializer (@Null JsonSerializer defaultSerializer) {
142 | this.defaultSerializer = defaultSerializer;
143 | }
144 |
145 | /** Registers a serializer to use for the specified type instead of the default behavior of serializing all of an objects
146 | * fields. */
147 | public void setSerializer (Class type, JsonSerializer serializer) {
148 | classToSerializer.put(type, serializer);
149 | }
150 |
151 | public JsonSerializer getSerializer (Class type) {
152 | return classToSerializer.get(type);
153 | }
154 |
155 | /** When true, field values that are identical to a newly constructed instance are not written. Default is true. */
156 | public void setUsePrototypes (boolean usePrototypes) {
157 | this.usePrototypes = usePrototypes;
158 | }
159 |
160 | /** Sets the type of elements in a collection. When the element type is known, the class for each element in the collection
161 | * does not need to be written unless different from the element type. */
162 | public void setElementType (Class type, String fieldName, Class elementType) {
163 | FieldMetadata metadata = getFields(type).get(fieldName);
164 | if (metadata == null) throw new JsonException("Field not found: " + fieldName + " (" + type.getName() + ")");
165 | metadata.elementType = elementType;
166 | }
167 |
168 | /** The specified field will be treated as if it has or does not have the {@link Deprecated} annotation.
169 | * @see #setIgnoreDeprecated(boolean)
170 | * @see #setReadDeprecated(boolean) */
171 | public void setDeprecated (Class type, String fieldName, boolean deprecated) {
172 | FieldMetadata metadata = getFields(type).get(fieldName);
173 | if (metadata == null) throw new JsonException("Field not found: " + fieldName + " (" + type.getName() + ")");
174 | metadata.deprecated = deprecated;
175 | }
176 |
177 | /** When true, fields are sorted alphabetically when written, otherwise the source code order is used. Default is false. */
178 | public void setSortFields (boolean sortFields) {
179 | this.sortFields = sortFields;
180 | }
181 |
182 | private OrderedMap getFields (Class type) {
183 | OrderedMap fields = typeToFields.get(type);
184 | if (fields != null) return fields;
185 |
186 | ArrayList classHierarchy = new ArrayList();
187 | Class nextClass = type;
188 | while (nextClass != Object.class) {
189 | classHierarchy.add(nextClass);
190 | nextClass = nextClass.getSuperclass();
191 | }
192 | ArrayList allFields = new ArrayList();
193 | for (int i = classHierarchy.size() - 1; i >= 0; i--)
194 | Collections.addAll(allFields, classHierarchy.get(i).getDeclaredFields());
195 |
196 | OrderedMap nameToField = new OrderedMap(allFields.size());
197 | for (int i = 0, n = allFields.size(); i < n; i++) {
198 | Field field = allFields.get(i);
199 |
200 | int modifiers = field.getModifiers();
201 | if (Modifier.isTransient(modifiers)) continue;
202 | if (Modifier.isStatic(modifiers)) continue;
203 | if (field.isSynthetic()) continue;
204 |
205 | if (!field.isAccessible()) {
206 | try {
207 | field.setAccessible(true);
208 | } catch (RuntimeException ex) {
209 | continue;
210 | }
211 | }
212 |
213 | nameToField.put(field.getName(), new FieldMetadata(field));
214 | }
215 | if (sortFields) Collections.sort(nameToField.keys);
216 | typeToFields.put(type, nameToField);
217 | return nameToField;
218 | }
219 |
220 | public String toJson (@Null Object object) {
221 | return toJson(object, object == null ? null : object.getClass(), (Class)null);
222 | }
223 |
224 | public String toJson (@Null Object object, @Null Class knownType) {
225 | return toJson(object, knownType, (Class)null);
226 | }
227 |
228 | /** @param knownType May be null if the type is unknown.
229 | * @param elementType May be null if the type is unknown. */
230 | public String toJson (@Null Object object, @Null Class knownType, @Null Class elementType) {
231 | StringWriter buffer = new StringWriter();
232 | toJson(object, knownType, elementType, buffer);
233 | return buffer.toString();
234 | }
235 |
236 | public void toJson (@Null Object object, File file) {
237 | toJson(object, object == null ? null : object.getClass(), null, file);
238 | }
239 |
240 | /** @param knownType May be null if the type is unknown. */
241 | public void toJson (@Null Object object, @Null Class knownType, File file) {
242 | toJson(object, knownType, null, file);
243 | }
244 |
245 | /** @param knownType May be null if the type is unknown.
246 | * @param elementType May be null if the type is unknown. */
247 | public void toJson (@Null Object object, @Null Class knownType, @Null Class elementType, File file) {
248 | Writer writer = null;
249 | try {
250 | writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
251 | toJson(object, knownType, elementType, writer);
252 | } catch (Exception ex) {
253 | throw new JsonException("Error writing file: " + file, ex);
254 | } finally {
255 | try {
256 | if (writer != null) writer.close();
257 | } catch (IOException ignored) {
258 | }
259 | }
260 | }
261 |
262 | public void toJson (@Null Object object, Writer writer) {
263 | toJson(object, object == null ? null : object.getClass(), null, writer);
264 | }
265 |
266 | /** @param knownType May be null if the type is unknown. */
267 | public void toJson (@Null Object object, @Null Class knownType, Writer writer) {
268 | toJson(object, knownType, null, writer);
269 | }
270 |
271 | /** @param knownType May be null if the type is unknown.
272 | * @param elementType May be null if the type is unknown. */
273 | public void toJson (@Null Object object, @Null Class knownType, @Null Class elementType, Writer writer) {
274 | setWriter(writer);
275 | try {
276 | writeValue(object, knownType, elementType);
277 | } finally {
278 | if (this.writer != null) {
279 | try {
280 | this.writer.close();
281 | } catch (IOException ignored) {
282 | }
283 | }
284 | this.writer = null;
285 | }
286 | }
287 |
288 | /** Sets the writer where JSON output will be written. This is only necessary when not using the toJson methods. */
289 | public void setWriter (Writer writer) {
290 | if (!(writer instanceof JsonWriter)) writer = new JsonWriter(writer);
291 | this.writer = (JsonWriter)writer;
292 | this.writer.setOutputType(outputType);
293 | this.writer.setQuoteLongValues(quoteLongValues);
294 | }
295 |
296 | public JsonWriter getWriter () {
297 | return writer;
298 | }
299 |
300 | /** Writes all fields of the specified object to the current JSON object. */
301 | public void writeFields (Object object) {
302 | Class type = object.getClass();
303 |
304 | Object[] defaultValues = getDefaultValues(type);
305 |
306 | OrderedMap fields = getFields(type);
307 | int defaultIndex = 0;
308 | ArrayList fieldNames = fields.orderedKeys();
309 | for (int i = 0, n = fieldNames.size(); i < n; i++) {
310 | FieldMetadata metadata = fields.get(fieldNames.get(i));
311 | if (ignoreDeprecated && metadata.deprecated) continue;
312 | Field field = metadata.field;
313 | try {
314 | Object value = field.get(object);
315 | if (defaultValues != null) {
316 | Object defaultValue = defaultValues[defaultIndex++];
317 | if (value == null && defaultValue == null) continue;
318 | if (value != null && defaultValue != null) {
319 | if (value.equals(defaultValue)) continue;
320 | if (value.getClass().isArray() && defaultValue.getClass().isArray()) {
321 | equals1[0] = value;
322 | equals2[0] = defaultValue;
323 | if (Arrays.deepEquals(equals1, equals2)) continue;
324 | }
325 | }
326 | }
327 |
328 | if (debug) System.out.println("Writing field: " + field.getName() + " (" + type.getName() + ")");
329 | writer.name(field.getName());
330 | writeValue(value, field.getType(), metadata.elementType);
331 | } catch (IllegalAccessException ex) {
332 | throw new JsonException("Error accessing field: " + field.getName() + " (" + type.getName() + ")", ex);
333 | } catch (JsonException ex) {
334 | ex.addTrace(field + " (" + type.getName() + ")");
335 | throw ex;
336 | } catch (Exception runtimeEx) {
337 | JsonException ex = new JsonException(runtimeEx);
338 | ex.addTrace(field + " (" + type.getName() + ")");
339 | throw ex;
340 | }
341 | }
342 | }
343 |
344 | private @Null Object[] getDefaultValues (Class type) {
345 | if (!usePrototypes) return null;
346 | if (classToDefaultValues.containsKey(type)) return classToDefaultValues.get(type);
347 | Object object;
348 | try {
349 | object = newInstance(type);
350 | } catch (Exception ex) {
351 | classToDefaultValues.put(type, null);
352 | return null;
353 | }
354 |
355 | OrderedMap fields = getFields(type);
356 | Object[] values = new Object[fields.size];
357 | classToDefaultValues.put(type, values);
358 |
359 | int defaultIndex = 0;
360 | ArrayList fieldNames = fields.orderedKeys();
361 | for (int i = 0, n = fieldNames.size(); i < n; i++) {
362 | FieldMetadata metadata = fields.get(fieldNames.get(i));
363 | if (ignoreDeprecated && metadata.deprecated) continue;
364 | Field field = metadata.field;
365 | try {
366 | values[defaultIndex++] = field.get(object);
367 | } catch (IllegalAccessException ex) {
368 | throw new JsonException("Error accessing field: " + field.getName() + " (" + type.getName() + ")", ex);
369 | } catch (JsonException ex) {
370 | ex.addTrace(field + " (" + type.getName() + ")");
371 | throw ex;
372 | } catch (RuntimeException runtimeEx) {
373 | JsonException ex = new JsonException(runtimeEx);
374 | ex.addTrace(field + " (" + type.getName() + ")");
375 | throw ex;
376 | }
377 | }
378 | return values;
379 | }
380 |
381 | /** @see #writeField(Object, String, String, Class) */
382 | public void writeField (Object object, String name) {
383 | writeField(object, name, name, null);
384 | }
385 |
386 | /** @param elementType May be null if the type is unknown.
387 | * @see #writeField(Object, String, String, Class) */
388 | public void writeField (Object object, String name, @Null Class elementType) {
389 | writeField(object, name, name, elementType);
390 | }
391 |
392 | /** @see #writeField(Object, String, String, Class) */
393 | public void writeField (Object object, String fieldName, String jsonName) {
394 | writeField(object, fieldName, jsonName, null);
395 | }
396 |
397 | /** Writes the specified field to the current JSON object.
398 | * @param elementType May be null if the type is unknown. */
399 | public void writeField (Object object, String fieldName, String jsonName, @Null Class elementType) {
400 | Class type = object.getClass();
401 | FieldMetadata metadata = getFields(type).get(fieldName);
402 | if (metadata == null) throw new JsonException("Field not found: " + fieldName + " (" + type.getName() + ")");
403 | Field field = metadata.field;
404 | if (elementType == null) elementType = metadata.elementType;
405 | try {
406 | if (debug) System.out.println("Writing field: " + field.getName() + " (" + type.getName() + ")");
407 | writer.name(jsonName);
408 | writeValue(field.get(object), field.getType(), elementType);
409 | } catch (IllegalAccessException ex) {
410 | throw new JsonException("Error accessing field: " + field.getName() + " (" + type.getName() + ")", ex);
411 | } catch (JsonException ex) {
412 | ex.addTrace(field + " (" + type.getName() + ")");
413 | throw ex;
414 | } catch (Exception runtimeEx) {
415 | JsonException ex = new JsonException(runtimeEx);
416 | ex.addTrace(field + " (" + type.getName() + ")");
417 | throw ex;
418 | }
419 | }
420 |
421 | /** Writes the value as a field on the current JSON object, without writing the actual class.
422 | * @param value May be null.
423 | * @see #writeValue(String, Object, Class, Class) */
424 | public void writeValue (String name, @Null Object value) {
425 | try {
426 | writer.name(name);
427 | } catch (IOException ex) {
428 | throw new JsonException(ex);
429 | }
430 | if (value == null)
431 | writeValue(value, null, null);
432 | else
433 | writeValue(value, value.getClass(), null);
434 | }
435 |
436 | /** Writes the value as a field on the current JSON object, writing the class of the object if it differs from the specified
437 | * known type.
438 | * @param value May be null.
439 | * @param knownType May be null if the type is unknown.
440 | * @see #writeValue(String, Object, Class, Class) */
441 | public void writeValue (String name, @Null Object value, @Null Class knownType) {
442 | try {
443 | writer.name(name);
444 | } catch (IOException ex) {
445 | throw new JsonException(ex);
446 | }
447 | writeValue(value, knownType, null);
448 | }
449 |
450 | /** Writes the value as a field on the current JSON object, writing the class of the object if it differs from the specified
451 | * known type. The specified element type is used as the default type for collections.
452 | * @param value May be null.
453 | * @param knownType May be null if the type is unknown.
454 | * @param elementType May be null if the type is unknown. */
455 | public void writeValue (String name, @Null Object value, @Null Class knownType, @Null Class elementType) {
456 | try {
457 | writer.name(name);
458 | } catch (IOException ex) {
459 | throw new JsonException(ex);
460 | }
461 | writeValue(value, knownType, elementType);
462 | }
463 |
464 | /** Writes the value, without writing the class of the object.
465 | * @param value May be null. */
466 | public void writeValue (@Null Object value) {
467 | if (value == null)
468 | writeValue(value, null, null);
469 | else
470 | writeValue(value, value.getClass(), null);
471 | }
472 |
473 | /** Writes the value, writing the class of the object if it differs from the specified known type.
474 | * @param value May be null.
475 | * @param knownType May be null if the type is unknown. */
476 | public void writeValue (@Null Object value, @Null Class knownType) {
477 | writeValue(value, knownType, null);
478 | }
479 |
480 | /** Writes the value, writing the class of the object if it differs from the specified known type. The specified element type
481 | * is used as the default type for collections.
482 | * @param value May be null.
483 | * @param knownType May be null if the type is unknown.
484 | * @param elementType May be null if the type is unknown. */
485 | public void writeValue (@Null Object value, @Null Class knownType, @Null Class elementType) {
486 | try {
487 | if (value == null) {
488 | writer.value(null);
489 | return;
490 | }
491 |
492 | if ((knownType != null && knownType.isPrimitive()) || knownType == String.class || knownType == Integer.class
493 | || knownType == Boolean.class || knownType == Float.class || knownType == Long.class || knownType == Double.class
494 | || knownType == Short.class || knownType == Byte.class || knownType == Character.class) {
495 | writer.value(value);
496 | return;
497 | }
498 |
499 | Class actualType = value.getClass();
500 |
501 | if (actualType.isPrimitive() || actualType == String.class || actualType == Integer.class || actualType == Boolean.class
502 | || actualType == Float.class || actualType == Long.class || actualType == Double.class || actualType == Short.class
503 | || actualType == Byte.class || actualType == Character.class) {
504 | writeObjectStart(actualType, null);
505 | writeValue("value", value);
506 | writeObjectEnd();
507 | return;
508 | }
509 |
510 | if (value instanceof JsonSerializable) {
511 | writeObjectStart(actualType, knownType);
512 | ((JsonSerializable)value).write(this);
513 | writeObjectEnd();
514 | return;
515 | }
516 |
517 | JsonSerializer serializer = classToSerializer.get(actualType);
518 | if (serializer != null) {
519 | serializer.write(this, value, knownType);
520 | return;
521 | }
522 |
523 | // JSON array special cases.
524 | if (value instanceof ArrayList) {
525 | if (knownType != null && actualType != knownType && actualType != ArrayList.class)
526 | throw new JsonException("Serialization of an Array other than the known type is not supported.\n" //
527 | + "Known type: " + knownType + "\nActual type: " + actualType);
528 | writeArrayStart();
529 | ArrayList array = (ArrayList)value;
530 | for (int i = 0, n = array.size(); i < n; i++)
531 | writeValue(array.get(i), elementType, null);
532 | writeArrayEnd();
533 | return;
534 | }
535 | if (value instanceof Collection) {
536 | if (typeName != null && actualType != ArrayList.class && (knownType == null || knownType != actualType)) {
537 | writeObjectStart(actualType, knownType);
538 | writeArrayStart("items");
539 | for (Object item : (Collection)value)
540 | writeValue(item, elementType, null);
541 | writeArrayEnd();
542 | writeObjectEnd();
543 | } else {
544 | writeArrayStart();
545 | for (Object item : (Collection)value)
546 | writeValue(item, elementType, null);
547 | writeArrayEnd();
548 | }
549 | return;
550 | }
551 | if (actualType.isArray()) {
552 | if (elementType == null) elementType = actualType.getComponentType();
553 | int length = Array.getLength(value);
554 | writeArrayStart();
555 | for (int i = 0; i < length; i++)
556 | writeValue(Array.get(value, i), elementType, null);
557 | writeArrayEnd();
558 | return;
559 | }
560 |
561 | // JSON object special cases.
562 | if (value instanceof ObjectMap) {
563 | if (knownType == null) knownType = ObjectMap.class;
564 | writeObjectStart(actualType, knownType);
565 | for (Entry entry : ((ObjectMap, ?>)value).entries()) {
566 | writer.name(convertToString(entry.key));
567 | writeValue(entry.value, elementType, null);
568 | }
569 | writeObjectEnd();
570 | return;
571 | }
572 | if (value instanceof Map) {
573 | if (knownType == null) knownType = HashMap.class;
574 | writeObjectStart(actualType, knownType);
575 | for (Map.Entry entry : ((Map, ?>)value).entrySet()) {
576 | writer.name(convertToString(entry.getKey()));
577 | writeValue(entry.getValue(), elementType, null);
578 | }
579 | writeObjectEnd();
580 | return;
581 | }
582 |
583 | // Enum special case.
584 | if (Enum.class.isAssignableFrom(actualType)) {
585 | if (typeName != null && (knownType == null || knownType != actualType)) {
586 | // Ensures that enums with specific implementations (abstract logic) serialize correctly.
587 | if (actualType.getEnumConstants() == null) actualType = actualType.getSuperclass();
588 |
589 | writeObjectStart(actualType, null);
590 | writer.name("value");
591 | writer.value(convertToString((Enum)value));
592 | writeObjectEnd();
593 | } else {
594 | writer.value(convertToString((Enum)value));
595 | }
596 | return;
597 | }
598 |
599 | writeObjectStart(actualType, knownType);
600 | writeFields(value);
601 | writeObjectEnd();
602 | } catch (IOException ex) {
603 | throw new JsonException(ex);
604 | }
605 | }
606 |
607 | public void writeObjectStart (String name) {
608 | try {
609 | writer.name(name);
610 | } catch (IOException ex) {
611 | throw new JsonException(ex);
612 | }
613 | writeObjectStart();
614 | }
615 |
616 | /** @param knownType May be null if the type is unknown. */
617 | public void writeObjectStart (String name, Class actualType, @Null Class knownType) {
618 | try {
619 | writer.name(name);
620 | } catch (IOException ex) {
621 | throw new JsonException(ex);
622 | }
623 | writeObjectStart(actualType, knownType);
624 | }
625 |
626 | public void writeObjectStart () {
627 | try {
628 | writer.object();
629 | } catch (IOException ex) {
630 | throw new JsonException(ex);
631 | }
632 | }
633 |
634 | /** Starts writing an object, writing the actualType to a field if needed.
635 | * @param knownType May be null if the type is unknown. */
636 | public void writeObjectStart (Class actualType, @Null Class knownType) {
637 | try {
638 | writer.object();
639 | } catch (IOException ex) {
640 | throw new JsonException(ex);
641 | }
642 | if (knownType == null || knownType != actualType) writeType(actualType);
643 | }
644 |
645 | public void writeObjectEnd () {
646 | try {
647 | writer.pop();
648 | } catch (IOException ex) {
649 | throw new JsonException(ex);
650 | }
651 | }
652 |
653 | public void writeArrayStart (String name) {
654 | try {
655 | writer.name(name);
656 | writer.array();
657 | } catch (IOException ex) {
658 | throw new JsonException(ex);
659 | }
660 | }
661 |
662 | public void writeArrayStart () {
663 | try {
664 | writer.array();
665 | } catch (IOException ex) {
666 | throw new JsonException(ex);
667 | }
668 | }
669 |
670 | public void writeArrayEnd () {
671 | try {
672 | writer.pop();
673 | } catch (IOException ex) {
674 | throw new JsonException(ex);
675 | }
676 | }
677 |
678 | public void writeType (Class type) {
679 | if (typeName == null) return;
680 | String className = getTag(type);
681 | if (className == null) className = type.getName();
682 | try {
683 | writer.set(typeName, className);
684 | } catch (IOException ex) {
685 | throw new JsonException(ex);
686 | }
687 | if (debug) System.out.println("Writing type: " + type.getName());
688 | }
689 |
690 | /** @param type May be null if the type is unknown.
691 | * @return May be null. */
692 | public @Null T fromJson (Class type, Reader reader) {
693 | return readValue(type, null, new JsonReader().parse(reader));
694 | }
695 |
696 | /** @param type May be null if the type is unknown.
697 | * @param elementType May be null if the type is unknown.
698 | * @return May be null. */
699 | public @Null T fromJson (Class type, Class elementType, Reader reader) {
700 | return readValue(type, elementType, new JsonReader().parse(reader));
701 | }
702 |
703 | /** @param type May be null if the type is unknown.
704 | * @return May be null. */
705 | public @Null T fromJson (Class type, InputStream input) {
706 | return readValue(type, null, new JsonReader().parse(input));
707 | }
708 |
709 | /** @param type May be null if the type is unknown.
710 | * @param elementType May be null if the type is unknown.
711 | * @return May be null. */
712 | public @Null T fromJson (Class type, Class elementType, InputStream input) {
713 | return readValue(type, elementType, new JsonReader().parse(input));
714 | }
715 |
716 | /** @param type May be null if the type is unknown.
717 | * @return May be null. */
718 | public @Null T fromJson (Class type, File file) {
719 | try {
720 | return readValue(type, null, new JsonReader().parse(file));
721 | } catch (Exception ex) {
722 | throw new JsonException("Error reading file: " + file, ex);
723 | }
724 | }
725 |
726 | /** @param type May be null if the type is unknown.
727 | * @param elementType May be null if the type is unknown.
728 | * @return May be null. */
729 | public @Null T fromJson (Class type, Class elementType, File file) {
730 | try {
731 | return readValue(type, elementType, new JsonReader().parse(file));
732 | } catch (Exception ex) {
733 | throw new JsonException("Error reading file: " + file, ex);
734 | }
735 | }
736 |
737 | /** @param type May be null if the type is unknown.
738 | * @return May be null. */
739 | public @Null T fromJson (Class type, char[] data, int offset, int length) {
740 | return readValue(type, null, new JsonReader().parse(data, offset, length));
741 | }
742 |
743 | /** @param type May be null if the type is unknown.
744 | * @param elementType May be null if the type is unknown.
745 | * @return May be null. */
746 | public @Null T fromJson (Class type, Class elementType, char[] data, int offset, int length) {
747 | return readValue(type, elementType, new JsonReader().parse(data, offset, length));
748 | }
749 |
750 | /** @param type May be null if the type is unknown.
751 | * @return May be null. */
752 | public @Null T fromJson (Class type, String json) {
753 | return readValue(type, null, new JsonReader().parse(json));
754 | }
755 |
756 | /** @param type May be null if the type is unknown.
757 | * @return May be null. */
758 | public @Null T fromJson (Class type, Class elementType, String json) {
759 | return readValue(type, elementType, new JsonReader().parse(json));
760 | }
761 |
762 | public void readField (Object object, String name, JsonValue jsonData) {
763 | readField(object, name, name, null, jsonData);
764 | }
765 |
766 | public void readField (Object object, String name, @Null Class elementType, JsonValue jsonData) {
767 | readField(object, name, name, elementType, jsonData);
768 | }
769 |
770 | public void readField (Object object, String fieldName, String jsonName, JsonValue jsonData) {
771 | readField(object, fieldName, jsonName, null, jsonData);
772 | }
773 |
774 | /** @param elementType May be null if the type is unknown. */
775 | public void readField (Object object, String fieldName, String jsonName, @Null Class elementType, JsonValue jsonMap) {
776 | Class type = object.getClass();
777 | FieldMetadata metadata = getFields(type).get(fieldName);
778 | if (metadata == null) throw new JsonException("Field not found: " + fieldName + " (" + type.getName() + ")");
779 | Field field = metadata.field;
780 | if (elementType == null) elementType = metadata.elementType;
781 | readField(object, field, jsonName, elementType, jsonMap);
782 | }
783 |
784 | /** @param object May be null if the field is static.
785 | * @param elementType May be null if the type is unknown. */
786 | public void readField (@Null Object object, Field field, String jsonName, @Null Class elementType, JsonValue jsonMap) {
787 | JsonValue jsonValue = jsonMap.get(jsonName);
788 | if (jsonValue == null) return;
789 | try {
790 | field.set(object, readValue(field.getType(), elementType, jsonValue));
791 | } catch (IllegalAccessException ex) {
792 | throw new JsonException("Error accessing field: " + field.getName() + " (" + field.getDeclaringClass().getName() + ")",
793 | ex);
794 | } catch (JsonException ex) {
795 | ex.addTrace(field.getName() + " (" + field.getDeclaringClass().getName() + ")");
796 | throw ex;
797 | } catch (RuntimeException runtimeEx) {
798 | JsonException ex = new JsonException(runtimeEx);
799 | ex.addTrace(jsonValue.trace());
800 | ex.addTrace(field.getName() + " (" + field.getDeclaringClass().getName() + ")");
801 | throw ex;
802 | }
803 | }
804 |
805 | public void readFields (Object object, JsonValue jsonMap) {
806 | Class type = object.getClass();
807 | OrderedMap fields = getFields(type);
808 | for (JsonValue child = jsonMap.child; child != null; child = child.next) {
809 | FieldMetadata metadata = fields.get(child.name().replace(" ", "_"));
810 | if (metadata == null) {
811 | if (child.name.equals(typeName)) continue;
812 | if (ignoreUnknownFields || ignoreUnknownField(type, child.name)) {
813 | if (debug) System.out.println("Ignoring unknown field: " + child.name + " (" + type.getName() + ")");
814 | continue;
815 | } else {
816 | JsonException ex = new JsonException("Field not found: " + child.name + " (" + type.getName() + ")");
817 | ex.addTrace(child.trace());
818 | throw ex;
819 | }
820 | } else {
821 | if (ignoreDeprecated && !readDeprecated && metadata.deprecated) continue;
822 | }
823 | Field field = metadata.field;
824 | try {
825 | field.set(object, readValue(field.getType(), metadata.elementType, child));
826 | } catch (IllegalAccessException ex) {
827 | throw new JsonException("Error accessing field: " + field.getName() + " (" + type.getName() + ")", ex);
828 | } catch (JsonException ex) {
829 | ex.addTrace(field.getName() + " (" + type.getName() + ")");
830 | throw ex;
831 | } catch (RuntimeException runtimeEx) {
832 | JsonException ex = new JsonException(runtimeEx);
833 | ex.addTrace(child.trace());
834 | ex.addTrace(field.getName() + " (" + type.getName() + ")");
835 | throw ex;
836 | }
837 | }
838 | }
839 |
840 | /** Called for each unknown field name encountered by {@link #readFields(Object, JsonValue)} when {@link #ignoreUnknownFields}
841 | * is false to determine whether the unknown field name should be ignored.
842 | * @param type The object type being read.
843 | * @param fieldName A field name encountered in the JSON for which there is no matching class field.
844 | * @return true if the field name should be ignored and an exception won't be thrown by
845 | * {@link #readFields(Object, JsonValue)}. */
846 | protected boolean ignoreUnknownField (Class type, String fieldName) {
847 | return false;
848 | }
849 |
850 | /** @param type May be null if the type is unknown.
851 | * @return May be null. */
852 | public @Null T readValue (String name, @Null Class type, JsonValue jsonMap) {
853 | return readValue(type, null, jsonMap.get(name));
854 | }
855 |
856 | /** @param type May be null if the type is unknown.
857 | * @return May be null. */
858 | public @Null T readValue (String name, @Null Class type, T defaultValue, JsonValue jsonMap) {
859 | JsonValue jsonValue = jsonMap.get(name);
860 | if (jsonValue == null) return defaultValue;
861 | return readValue(type, null, jsonValue);
862 | }
863 |
864 | /** @param type May be null if the type is unknown.
865 | * @param elementType May be null if the type is unknown.
866 | * @return May be null. */
867 | public @Null T readValue (String name, @Null Class type, @Null Class elementType, JsonValue jsonMap) {
868 | return readValue(type, elementType, jsonMap.get(name));
869 | }
870 |
871 | /** @param type May be null if the type is unknown.
872 | * @param elementType May be null if the type is unknown.
873 | * @return May be null. */
874 | public @Null T readValue (String name, @Null Class type, @Null Class elementType, T defaultValue, JsonValue jsonMap) {
875 | JsonValue jsonValue = jsonMap.get(name);
876 | return readValue(type, elementType, defaultValue, jsonValue);
877 | }
878 |
879 | /** @param type May be null if the type is unknown.
880 | * @param elementType May be null if the type is unknown.
881 | * @return May be null. */
882 | public @Null T readValue (@Null Class type, @Null Class elementType, T defaultValue, JsonValue jsonData) {
883 | if (jsonData == null) return defaultValue;
884 | return readValue(type, elementType, jsonData);
885 | }
886 |
887 | /** @param type May be null if the type is unknown.
888 | * @return May be null. */
889 | public @Null T readValue (@Null Class type, JsonValue jsonData) {
890 | return readValue(type, null, jsonData);
891 | }
892 |
893 | /** @param type May be null if the type is unknown.
894 | * @param elementType May be null if the type is unknown.
895 | * @return May be null. */
896 | public @Null T readValue (@Null Class type, @Null Class elementType, JsonValue jsonData) {
897 | if (jsonData == null) return null;
898 |
899 | if (jsonData.isObject()) {
900 | String className = typeName == null ? null : jsonData.getString(typeName, null);
901 | if (className != null) {
902 | type = getClass(className);
903 | if (type == null) {
904 | try {
905 | type = (Class)Class.forName(className);
906 | } catch (ClassNotFoundException ex) {
907 | throw new JsonException(ex);
908 | }
909 | }
910 | }
911 |
912 | if (type == null) {
913 | if (defaultSerializer != null) return (T)defaultSerializer.read(this, jsonData, type);
914 | return (T)jsonData;
915 | }
916 |
917 | if (typeName != null && Collection.class.isAssignableFrom(type)) {
918 | // JSON object wrapper to specify type.
919 | jsonData = jsonData.get("items");
920 | if (jsonData == null)
921 | throw new JsonException("Unable to convert object to collection: " + jsonData + " (" + type.getName() + ")");
922 | } else {
923 | JsonSerializer serializer = classToSerializer.get(type);
924 | if (serializer != null) return (T)serializer.read(this, jsonData, type);
925 |
926 | if (type == String.class || type == Integer.class || type == Boolean.class || type == Float.class
927 | || type == Long.class || type == Double.class || type == Short.class || type == Byte.class
928 | || type == Character.class || Enum.class.isAssignableFrom(type)) {
929 | return readValue("value", type, jsonData);
930 | }
931 |
932 | Object object = newInstance(type);
933 |
934 | if (object instanceof JsonSerializable) {
935 | ((JsonSerializable)object).read(this, jsonData);
936 | return (T)object;
937 | }
938 |
939 | // JSON object special cases.
940 | if (object instanceof ObjectMap) {
941 | ObjectMap result = (ObjectMap)object;
942 | for (JsonValue child = jsonData.child; child != null; child = child.next)
943 | result.put(child.name, readValue(elementType, null, child));
944 | return (T)result;
945 | }
946 | if (object instanceof Map) {
947 | Map result = (Map)object;
948 | for (JsonValue child = jsonData.child; child != null; child = child.next) {
949 | if (child.name.equals(typeName)) continue;
950 | result.put(child.name, readValue(elementType, null, child));
951 | }
952 | return (T)result;
953 | }
954 |
955 | readFields(object, jsonData);
956 | return (T)object;
957 | }
958 | }
959 |
960 | if (type != null) {
961 | JsonSerializer serializer = classToSerializer.get(type);
962 | if (serializer != null) return (T)serializer.read(this, jsonData, type);
963 |
964 | if (JsonSerializable.class.isAssignableFrom(type)) {
965 | // A Serializable may be read as an array, string, etc, even though it will be written as an object.
966 | Object object = newInstance(type);
967 | ((JsonSerializable)object).read(this, jsonData);
968 | return (T)object;
969 | }
970 | }
971 |
972 | if (jsonData.isArray()) {
973 | // JSON array special cases.
974 | if (type == null || type == Object.class) type = (Class)ArrayList.class;
975 | if (Collection.class.isAssignableFrom(type)) {
976 | Collection result = type.isInterface() ? new ArrayList() : (Collection)newInstance(type);
977 | for (JsonValue child = jsonData.child; child != null; child = child.next)
978 | result.add(readValue(elementType, null, child));
979 | return (T)result;
980 | }
981 | if (type.isArray()) {
982 | Class componentType = type.getComponentType();
983 | if (elementType == null) elementType = componentType;
984 | Object result = Array.newInstance(componentType, jsonData.size);
985 | int i = 0;
986 | for (JsonValue child = jsonData.child; child != null; child = child.next)
987 | Array.set(result, i++, readValue(elementType, null, child));
988 | return (T)result;
989 | }
990 | throw new JsonException("Unable to convert value to required type: " + jsonData + " (" + type.getName() + ")");
991 | }
992 |
993 | if (jsonData.isNumber()) {
994 | try {
995 | if (type == null || type == float.class || type == Float.class) return (T)(Float)jsonData.asFloat();
996 | if (type == int.class || type == Integer.class) return (T)(Integer)jsonData.asInt();
997 | if (type == long.class || type == Long.class) return (T)(Long)jsonData.asLong();
998 | if (type == double.class || type == Double.class) return (T)(Double)jsonData.asDouble();
999 | if (type == String.class) return (T)jsonData.asString();
1000 | if (type == short.class || type == Short.class) return (T)(Short)jsonData.asShort();
1001 | if (type == byte.class || type == Byte.class) return (T)(Byte)jsonData.asByte();
1002 | } catch (NumberFormatException ignored) {
1003 | }
1004 | jsonData = new JsonValue(jsonData.asString());
1005 | }
1006 |
1007 | if (jsonData.isBoolean()) {
1008 | try {
1009 | if (type == null || type == boolean.class || type == Boolean.class) return (T)(Boolean)jsonData.asBoolean();
1010 | } catch (NumberFormatException ignored) {
1011 | }
1012 | jsonData = new JsonValue(jsonData.asString());
1013 | }
1014 |
1015 | if (jsonData.isString()) {
1016 | String string = jsonData.asString();
1017 | if (type == null || type == String.class) return (T)string;
1018 | try {
1019 | if (type == int.class || type == Integer.class) return (T)Integer.valueOf(string);
1020 | if (type == float.class || type == Float.class) return (T)Float.valueOf(string);
1021 | if (type == long.class || type == Long.class) return (T)Long.valueOf(string);
1022 | if (type == double.class || type == Double.class) return (T)Double.valueOf(string);
1023 | if (type == short.class || type == Short.class) return (T)Short.valueOf(string);
1024 | if (type == byte.class || type == Byte.class) return (T)Byte.valueOf(string);
1025 | } catch (NumberFormatException ignored) {
1026 | }
1027 | if (type == boolean.class || type == Boolean.class) return (T)Boolean.valueOf(string);
1028 | if (type == char.class || type == Character.class) return (T)(Character)string.charAt(0);
1029 | if (Enum.class.isAssignableFrom(type)) {
1030 | Enum[] constants = (Enum[])type.getEnumConstants();
1031 | for (int i = 0, n = constants.length; i < n; i++) {
1032 | Enum e = constants[i];
1033 | if (string.equals(convertToString(e))) return (T)e;
1034 | }
1035 | }
1036 | if (type == CharSequence.class) return (T)string;
1037 | throw new JsonException("Unable to convert value to required type: " + jsonData + " (" + type.getName() + ")");
1038 | }
1039 |
1040 | return null;
1041 | }
1042 |
1043 | /** Each field on the to object is set to the value for the field with the same name on the from
1044 | * object. The to object must have at least all the fields of the from object with the same name and
1045 | * type. */
1046 | public void copyFields (Object from, Object to) {
1047 | OrderedMap toFields = getFields(to.getClass());
1048 | for (ObjectMap.Entry entry : getFields(from.getClass())) {
1049 | FieldMetadata toField = toFields.get(entry.key);
1050 | Field fromField = entry.value.field;
1051 | if (toField == null) throw new JsonException("To object is missing field: " + entry.key);
1052 | try {
1053 | toField.field.set(to, fromField.get(from));
1054 | } catch (IllegalAccessException ex) {
1055 | throw new JsonException("Error copying field: " + fromField.getName(), ex);
1056 | }
1057 | }
1058 | }
1059 |
1060 | private String convertToString (Enum e) {
1061 | return enumNames ? e.name() : e.toString();
1062 | }
1063 |
1064 | private String convertToString (Object object) {
1065 | if (object instanceof Enum) return convertToString((Enum)object);
1066 | if (object instanceof Class) return ((Class)object).getName();
1067 | return String.valueOf(object);
1068 | }
1069 |
1070 | protected Object newInstance (Class type) {
1071 | try {
1072 | return type.newInstance();
1073 | } catch (Exception ex) {
1074 | try {
1075 | // Try a private constructor.
1076 | Constructor constructor = type.getDeclaredConstructor();
1077 | constructor.setAccessible(true);
1078 | return constructor.newInstance();
1079 | } catch (SecurityException ignored) {
1080 | } catch (IllegalAccessException ignored) {
1081 | if (Enum.class.isAssignableFrom(type)) {
1082 | if (type.getEnumConstants() == null) type = type.getSuperclass();
1083 | return type.getEnumConstants()[0];
1084 | }
1085 | if (type.isArray())
1086 | throw new JsonException("Encountered JSON object when expected array of type: " + type.getName(), ex);
1087 | else if (type.isMemberClass() && !Modifier.isStatic(type.getModifiers()))
1088 | throw new JsonException("Class cannot be created (non-static member class): " + type.getName(), ex);
1089 | else
1090 | throw new JsonException("Class cannot be created (missing no-arg constructor): " + type.getName(), ex);
1091 | } catch (Exception privateConstructorException) {
1092 | ex = privateConstructorException;
1093 | }
1094 | throw new JsonException("Error constructing instance of class: " + type.getName(), ex);
1095 | }
1096 | }
1097 |
1098 | public String prettyPrint (@Null Object object) {
1099 | return prettyPrint(object, 0);
1100 | }
1101 |
1102 | public String prettyPrint (String json) {
1103 | return prettyPrint(json, 0);
1104 | }
1105 |
1106 | public String prettyPrint (@Null Object object, int singleLineColumns) {
1107 | return prettyPrint(toJson(object), singleLineColumns);
1108 | }
1109 |
1110 | public String prettyPrint (String json, int singleLineColumns) {
1111 | return new JsonReader().parse(json).prettyPrint(outputType, singleLineColumns);
1112 | }
1113 |
1114 | public String prettyPrint (@Null Object object, PrettyPrintSettings settings) {
1115 | return prettyPrint(toJson(object), settings);
1116 | }
1117 |
1118 | public String prettyPrint (String json, PrettyPrintSettings settings) {
1119 | return new JsonReader().parse(json).prettyPrint(settings);
1120 | }
1121 |
1122 | static private class FieldMetadata {
1123 | final Field field;
1124 | Class elementType;
1125 | boolean deprecated;
1126 |
1127 | public FieldMetadata (Field field) {
1128 | this.field = field;
1129 | int index = (ObjectMap.class.isAssignableFrom(field.getType()) || Map.class.isAssignableFrom(field.getType())) ? 1 : 0;
1130 | this.elementType = getElementType(field, index);
1131 | deprecated = field.isAnnotationPresent(Deprecated.class);
1132 | }
1133 |
1134 | /** If the type of the field is parameterized, returns the Class object representing the parameter type at the specified
1135 | * index, null otherwise. */
1136 | static private @Null Class getElementType (Field field, int index) {
1137 | Type genericType = field.getGenericType();
1138 | if (genericType instanceof ParameterizedType) {
1139 | Type[] actualTypes = ((ParameterizedType)genericType).getActualTypeArguments();
1140 | if (actualTypes.length - 1 >= index) {
1141 | Type actualType = actualTypes[index];
1142 | if (actualType instanceof Class)
1143 | return (Class)actualType;
1144 | else if (actualType instanceof ParameterizedType)
1145 | return (Class)((ParameterizedType)actualType).getRawType();
1146 | else if (actualType instanceof GenericArrayType) {
1147 | Type componentType = ((GenericArrayType)actualType).getGenericComponentType();
1148 | if (componentType instanceof Class) return Array.newInstance((Class)componentType, 0).getClass();
1149 | }
1150 | }
1151 | }
1152 | return null;
1153 | }
1154 | }
1155 | }
1156 |
--------------------------------------------------------------------------------
/src/com/esotericsoftware/jsonbeans/JsonException.java:
--------------------------------------------------------------------------------
1 |
2 | package com.esotericsoftware.jsonbeans;
3 |
4 | /** Indicates an error during serialization due to misconfiguration or during deserialization due to invalid input data.
5 | * @author Nathan Sweet */
6 | public class JsonException extends RuntimeException {
7 | private StringBuilder trace;
8 |
9 | public JsonException () {
10 | super();
11 | }
12 |
13 | public JsonException (String message, Throwable cause) {
14 | super(message, cause);
15 | }
16 |
17 | public JsonException (String message) {
18 | super(message);
19 | }
20 |
21 | public JsonException (Throwable cause) {
22 | super("", cause);
23 | }
24 |
25 | /** Returns true if any of the exceptions that caused this exception are of the specified type. */
26 | public boolean causedBy (Class type) {
27 | return causedBy(this, type);
28 | }
29 |
30 | private boolean causedBy (Throwable ex, Class type) {
31 | Throwable cause = ex.getCause();
32 | if (cause == null || cause == ex) return false;
33 | if (type.isAssignableFrom(cause.getClass())) return true;
34 | return causedBy(cause, type);
35 | }
36 |
37 | public String getMessage () {
38 | if (trace == null) return super.getMessage();
39 | StringBuilder sb = new StringBuilder(512);
40 | sb.append(super.getMessage());
41 | if (sb.length() > 0) sb.append('\n');
42 | sb.append("Serialization trace:");
43 | sb.append(trace);
44 | return sb.toString();
45 | }
46 |
47 | /** Adds information to the exception message about where in the the object graph serialization failure occurred. Serializers
48 | * can catch {@link JsonException}, add trace information, and rethrow the exception. */
49 | public void addTrace (String info) {
50 | if (info == null) throw new IllegalArgumentException("info cannot be null.");
51 | if (trace == null) trace = new StringBuilder(512);
52 | trace.append('\n');
53 | trace.append(info);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/com/esotericsoftware/jsonbeans/JsonReader.java:
--------------------------------------------------------------------------------
1 | // line 1 "JsonReader.rl"
2 | // Do not edit this file! Generated by Ragel.
3 | // Ragel.exe -G2 -J -o JsonReader.java JsonReader.rl
4 | /*******************************************************************************
5 | * Copyright 2011 See AUTHORS file.
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | ******************************************************************************/
19 |
20 | package com.esotericsoftware.jsonbeans;
21 |
22 | import java.io.File;
23 | import java.io.FileInputStream;
24 | import java.io.IOException;
25 | import java.io.InputStream;
26 | import java.io.InputStreamReader;
27 | import java.io.Reader;
28 | import java.util.ArrayList;
29 |
30 | import com.esotericsoftware.jsonbeans.JsonValue.ValueType;
31 |
32 | /** Lightweight JSON parser.
33 | *
34 | * The default behavior is to parse the JSON into a DOM containing {@link JsonValue} objects. Extend this class and override
35 | * methods to perform event driven parsing. When this is done, the parse methods will return null.
36 | * @author Nathan Sweet */
37 | public class JsonReader {
38 | public JsonValue parse (String json) {
39 | char[] data = json.toCharArray();
40 | return parse(data, 0, data.length);
41 | }
42 |
43 | public JsonValue parse (Reader reader) {
44 | try {
45 | char[] data = new char[1024];
46 | int offset = 0;
47 | while (true) {
48 | int length = reader.read(data, offset, data.length - offset);
49 | if (length == -1) break;
50 | if (length == 0) {
51 | char[] newData = new char[data.length * 2];
52 | System.arraycopy(data, 0, newData, 0, data.length);
53 | data = newData;
54 | } else
55 | offset += length;
56 | }
57 | return parse(data, 0, offset);
58 | } catch (IOException ex) {
59 | throw new JsonException(ex);
60 | } finally {
61 | try {
62 | reader.close();
63 | } catch (IOException ignored) {
64 | }
65 | }
66 | }
67 |
68 | public JsonValue parse (InputStream input) {
69 | try {
70 | return parse(new InputStreamReader(input, "UTF-8"));
71 | } catch (IOException ex) {
72 | throw new JsonException(ex);
73 | } finally {
74 | try {
75 | input.close();
76 | } catch (IOException ignored) {
77 | }
78 | }
79 | }
80 |
81 | public JsonValue parse (File file) {
82 | try {
83 | return parse(new InputStreamReader(new FileInputStream(file), "UTF-8"));
84 | } catch (Exception ex) {
85 | throw new JsonException("Error parsing file: " + file, ex);
86 | }
87 | }
88 |
89 | public JsonValue parse (char[] data, int offset, int length) {
90 | int cs, p = offset, pe = length, eof = pe, top = 0;
91 | int[] stack = new int[4];
92 |
93 | int s = 0;
94 | ArrayList names = new ArrayList(8);
95 | boolean needsUnescape = false, stringIsName = false, stringIsUnquoted = false;
96 | RuntimeException parseRuntimeEx = null;
97 |
98 | boolean debug = false;
99 | if (debug) System.out.println();
100 |
101 | try {
102 |
103 | // line 3 "JsonReader.java"
104 | {
105 | cs = json_start;
106 | top = 0;
107 | }
108 |
109 | // line 8 "JsonReader.java"
110 | {
111 | int _klen;
112 | int _trans = 0;
113 | int _acts;
114 | int _nacts;
115 | int _keys;
116 | int _goto_targ = 0;
117 |
118 | _goto:
119 | while (true) {
120 | switch (_goto_targ) {
121 | case 0:
122 | if (p == pe) {
123 | _goto_targ = 4;
124 | continue _goto;
125 | }
126 | if (cs == 0) {
127 | _goto_targ = 5;
128 | continue _goto;
129 | }
130 | case 1:
131 | _match:
132 | do {
133 | _keys = _json_key_offsets[cs];
134 | _trans = _json_index_offsets[cs];
135 | _klen = _json_single_lengths[cs];
136 | if (_klen > 0) {
137 | int _lower = _keys;
138 | int _mid;
139 | int _upper = _keys + _klen - 1;
140 | while (true) {
141 | if (_upper < _lower) break;
142 |
143 | _mid = _lower + ((_upper - _lower) >> 1);
144 | if (data[p] < _json_trans_keys[_mid])
145 | _upper = _mid - 1;
146 | else if (data[p] > _json_trans_keys[_mid])
147 | _lower = _mid + 1;
148 | else {
149 | _trans += (_mid - _keys);
150 | break _match;
151 | }
152 | }
153 | _keys += _klen;
154 | _trans += _klen;
155 | }
156 |
157 | _klen = _json_range_lengths[cs];
158 | if (_klen > 0) {
159 | int _lower = _keys;
160 | int _mid;
161 | int _upper = _keys + (_klen << 1) - 2;
162 | while (true) {
163 | if (_upper < _lower) break;
164 |
165 | _mid = _lower + (((_upper - _lower) >> 1) & ~1);
166 | if (data[p] < _json_trans_keys[_mid])
167 | _upper = _mid - 2;
168 | else if (data[p] > _json_trans_keys[_mid + 1])
169 | _lower = _mid + 2;
170 | else {
171 | _trans += ((_mid - _keys) >> 1);
172 | break _match;
173 | }
174 | }
175 | _trans += _klen;
176 | }
177 | } while (false);
178 |
179 | _trans = _json_indicies[_trans];
180 | cs = _json_trans_targs[_trans];
181 |
182 | if (_json_trans_actions[_trans] != 0) {
183 | _acts = _json_trans_actions[_trans];
184 | _nacts = (int)_json_actions[_acts++];
185 | while (_nacts-- > 0) {
186 | switch (_json_actions[_acts++]) {
187 | case 0:
188 | // line 110 "JsonReader.rl"
189 | {
190 | stringIsName = true;
191 | }
192 | break;
193 | case 1:
194 | // line 113 "JsonReader.rl"
195 | {
196 | String value = new String(data, s, p - s);
197 | if (needsUnescape) value = unescape(value);
198 | outer:
199 | if (stringIsName) {
200 | stringIsName = false;
201 | if (debug) System.out.println("name: " + value);
202 | names.add(value);
203 | } else {
204 | String name = names.size() > 0 ? names.remove(names.size() - 1) : null;
205 | if (stringIsUnquoted) {
206 | if (value.equals("true")) {
207 | if (debug) System.out.println("boolean: " + name + "=true");
208 | bool(name, true);
209 | break outer;
210 | } else if (value.equals("false")) {
211 | if (debug) System.out.println("boolean: " + name + "=false");
212 | bool(name, false);
213 | break outer;
214 | } else if (value.equals("null")) {
215 | string(name, null);
216 | break outer;
217 | }
218 | boolean couldBeDouble = false, couldBeLong = true;
219 | outer2:
220 | for (int i = s; i < p; i++) {
221 | switch (data[i]) {
222 | case '0':
223 | case '1':
224 | case '2':
225 | case '3':
226 | case '4':
227 | case '5':
228 | case '6':
229 | case '7':
230 | case '8':
231 | case '9':
232 | case '-':
233 | case '+':
234 | break;
235 | case '.':
236 | case 'e':
237 | case 'E':
238 | couldBeDouble = true;
239 | couldBeLong = false;
240 | break;
241 | default:
242 | couldBeDouble = false;
243 | couldBeLong = false;
244 | break outer2;
245 | }
246 | }
247 | if (couldBeDouble) {
248 | try {
249 | if (debug) System.out.println("double: " + name + "=" + Double.parseDouble(value));
250 | number(name, Double.parseDouble(value), value);
251 | break outer;
252 | } catch (NumberFormatException ignored) {
253 | }
254 | } else if (couldBeLong) {
255 | if (debug) System.out.println("double: " + name + "=" + Double.parseDouble(value));
256 | try {
257 | number(name, Long.parseLong(value), value);
258 | break outer;
259 | } catch (NumberFormatException ignored) {
260 | }
261 | }
262 | }
263 | if (debug) System.out.println("string: " + name + "=" + value);
264 | string(name, value);
265 | }
266 | stringIsUnquoted = false;
267 | s = p;
268 | }
269 | break;
270 | case 2:
271 | // line 187 "JsonReader.rl"
272 | {
273 | String name = names.size() > 0 ? names.remove(names.size() - 1) : null;
274 | if (debug) System.out.println("startObject: " + name);
275 | startObject(name);
276 | {
277 | if (top == stack.length) {
278 | int[] newStack = new int[stack.length * 2];
279 | System.arraycopy(stack, 0, newStack, 0, stack.length);
280 | stack = newStack;
281 | }
282 | {
283 | stack[top++] = cs;
284 | cs = 5;
285 | _goto_targ = 2;
286 | if (true) continue _goto;
287 | }
288 | }
289 | }
290 | break;
291 | case 3:
292 | // line 193 "JsonReader.rl"
293 | {
294 | if (debug) System.out.println("endObject");
295 | pop();
296 | {
297 | cs = stack[--top];
298 | _goto_targ = 2;
299 | if (true) continue _goto;
300 | }
301 | }
302 | break;
303 | case 4:
304 | // line 198 "JsonReader.rl"
305 | {
306 | String name = names.size() > 0 ? names.remove(names.size() - 1) : null;
307 | if (debug) System.out.println("startArray: " + name);
308 | startArray(name);
309 | {
310 | if (top == stack.length) {
311 | int[] newStack = new int[stack.length * 2];
312 | System.arraycopy(stack, 0, newStack, 0, stack.length);
313 | stack = newStack;
314 | }
315 | {
316 | stack[top++] = cs;
317 | cs = 23;
318 | _goto_targ = 2;
319 | if (true) continue _goto;
320 | }
321 | }
322 | }
323 | break;
324 | case 5:
325 | // line 204 "JsonReader.rl"
326 | {
327 | if (debug) System.out.println("endArray");
328 | pop();
329 | {
330 | cs = stack[--top];
331 | _goto_targ = 2;
332 | if (true) continue _goto;
333 | }
334 | }
335 | break;
336 | case 6:
337 | // line 209 "JsonReader.rl"
338 | {
339 | int start = p - 1;
340 | if (data[p++] == '/') {
341 | while (p != eof && data[p] != '\n')
342 | p++;
343 | p--;
344 | } else {
345 | while (p + 1 < eof && data[p] != '*' || data[p + 1] != '/')
346 | p++;
347 | p++;
348 | }
349 | if (debug) System.out.println("comment " + new String(data, start, p - start));
350 | }
351 | break;
352 | case 7:
353 | // line 222 "JsonReader.rl"
354 | {
355 | if (debug) System.out.println("unquotedChars");
356 | s = p;
357 | needsUnescape = false;
358 | stringIsUnquoted = true;
359 | if (stringIsName) {
360 | outer:
361 | while (true) {
362 | switch (data[p]) {
363 | case '\\':
364 | needsUnescape = true;
365 | break;
366 | case '/':
367 | if (p + 1 == eof) break;
368 | char c = data[p + 1];
369 | if (c == '/' || c == '*') break outer;
370 | break;
371 | case ':':
372 | case '\r':
373 | case '\n':
374 | break outer;
375 | }
376 | if (debug) System.out.println("unquotedChar (name): '" + data[p] + "'");
377 | p++;
378 | if (p == eof) break;
379 | }
380 | } else {
381 | outer:
382 | while (true) {
383 | switch (data[p]) {
384 | case '\\':
385 | needsUnescape = true;
386 | break;
387 | case '/':
388 | if (p + 1 == eof) break;
389 | char c = data[p + 1];
390 | if (c == '/' || c == '*') break outer;
391 | break;
392 | case '}':
393 | case ']':
394 | case ',':
395 | case '\r':
396 | case '\n':
397 | break outer;
398 | }
399 | if (debug) System.out.println("unquotedChar (value): '" + data[p] + "'");
400 | p++;
401 | if (p == eof) break;
402 | }
403 | }
404 | p--;
405 | while (Character.isSpace(data[p]))
406 | p--;
407 | }
408 | break;
409 | case 8:
410 | // line 276 "JsonReader.rl"
411 | {
412 | if (debug) System.out.println("quotedChars");
413 | s = ++p;
414 | needsUnescape = false;
415 | outer:
416 | while (true) {
417 | switch (data[p]) {
418 | case '\\':
419 | needsUnescape = true;
420 | p++;
421 | break;
422 | case '"':
423 | break outer;
424 | }
425 | // if (debug) System.out.println("quotedChar: '" + data[p] + "'");
426 | p++;
427 | if (p == eof) break;
428 | }
429 | p--;
430 | }
431 | break;
432 | // line 313 "JsonReader.java"
433 | }
434 | }
435 | }
436 |
437 | case 2:
438 | if (cs == 0) {
439 | _goto_targ = 5;
440 | continue _goto;
441 | }
442 | if (++p != pe) {
443 | _goto_targ = 1;
444 | continue _goto;
445 | }
446 | case 4:
447 | if (p == eof) {
448 | int __acts = _json_eof_actions[cs];
449 | int __nacts = (int)_json_actions[__acts++];
450 | while (__nacts-- > 0) {
451 | switch (_json_actions[__acts++]) {
452 | case 1:
453 | // line 113 "JsonReader.rl"
454 | {
455 | String value = new String(data, s, p - s);
456 | if (needsUnescape) value = unescape(value);
457 | outer:
458 | if (stringIsName) {
459 | stringIsName = false;
460 | if (debug) System.out.println("name: " + value);
461 | names.add(value);
462 | } else {
463 | String name = names.size() > 0 ? names.remove(names.size() - 1) : null;
464 | if (stringIsUnquoted) {
465 | if (value.equals("true")) {
466 | if (debug) System.out.println("boolean: " + name + "=true");
467 | bool(name, true);
468 | break outer;
469 | } else if (value.equals("false")) {
470 | if (debug) System.out.println("boolean: " + name + "=false");
471 | bool(name, false);
472 | break outer;
473 | } else if (value.equals("null")) {
474 | string(name, null);
475 | break outer;
476 | }
477 | boolean couldBeDouble = false, couldBeLong = true;
478 | outer2:
479 | for (int i = s; i < p; i++) {
480 | switch (data[i]) {
481 | case '0':
482 | case '1':
483 | case '2':
484 | case '3':
485 | case '4':
486 | case '5':
487 | case '6':
488 | case '7':
489 | case '8':
490 | case '9':
491 | case '-':
492 | case '+':
493 | break;
494 | case '.':
495 | case 'e':
496 | case 'E':
497 | couldBeDouble = true;
498 | couldBeLong = false;
499 | break;
500 | default:
501 | couldBeDouble = false;
502 | couldBeLong = false;
503 | break outer2;
504 | }
505 | }
506 | if (couldBeDouble) {
507 | try {
508 | if (debug) System.out.println("double: " + name + "=" + Double.parseDouble(value));
509 | number(name, Double.parseDouble(value), value);
510 | break outer;
511 | } catch (NumberFormatException ignored) {
512 | }
513 | } else if (couldBeLong) {
514 | if (debug) System.out.println("double: " + name + "=" + Double.parseDouble(value));
515 | try {
516 | number(name, Long.parseLong(value), value);
517 | break outer;
518 | } catch (NumberFormatException ignored) {
519 | }
520 | }
521 | }
522 | if (debug) System.out.println("string: " + name + "=" + value);
523 | string(name, value);
524 | }
525 | stringIsUnquoted = false;
526 | s = p;
527 | }
528 | break;
529 | // line 411 "JsonReader.java"
530 | }
531 | }
532 | }
533 |
534 | case 5:
535 | }
536 | break;
537 | }
538 | }
539 |
540 | // line 312 "JsonReader.rl"
541 |
542 | } catch (RuntimeException ex) {
543 | parseRuntimeEx = ex;
544 | }
545 |
546 | JsonValue root = this.root;
547 | this.root = null;
548 | current = null;
549 | lastChild.clear();
550 |
551 | if (p < pe) {
552 | int lineNumber = 1;
553 | for (int i = 0; i < p; i++)
554 | if (data[i] == '\n') lineNumber++;
555 | int start = Math.max(0, p - 32);
556 | throw new JsonException("Error parsing JSON on line " + lineNumber + " near: " + new String(data, start, p - start)
557 | + "*ERROR*" + new String(data, p, Math.min(64, pe - p)), parseRuntimeEx);
558 | }
559 | if (elements.size() != 0) {
560 | JsonValue element = elements.get(elements.size() - 1);
561 | elements.clear();
562 | if (element != null && element.isObject())
563 | throw new JsonException("Error parsing JSON, unmatched brace.");
564 | else
565 | throw new JsonException("Error parsing JSON, unmatched bracket.");
566 | }
567 | if (parseRuntimeEx != null) throw new JsonException("Error parsing JSON: " + new String(data), parseRuntimeEx);
568 | return root;
569 | }
570 |
571 | // line 421 "JsonReader.java"
572 | private static byte[] init__json_actions_0 () {
573 | return new byte[] {0, 1, 1, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, 7, 1, 8, 2, 0, 7, 2, 0, 8, 2, 1, 3, 2, 1, 5};
574 | }
575 |
576 | private static final byte _json_actions[] = init__json_actions_0();
577 |
578 | private static short[] init__json_key_offsets_0 () {
579 | return new short[] {0, 0, 11, 13, 14, 16, 25, 31, 37, 39, 50, 57, 64, 73, 74, 83, 85, 87, 96, 98, 100, 101, 103, 105, 116,
580 | 123, 130, 141, 142, 153, 155, 157, 168, 170, 172, 174, 179, 184, 184};
581 | }
582 |
583 | private static final short _json_key_offsets[] = init__json_key_offsets_0();
584 |
585 | private static char[] init__json_trans_keys_0 () {
586 | return new char[] {13, 32, 34, 44, 47, 58, 91, 93, 123, 9, 10, 42, 47, 34, 42, 47, 13, 32, 34, 44, 47, 58, 125, 9, 10, 13,
587 | 32, 47, 58, 9, 10, 13, 32, 47, 58, 9, 10, 42, 47, 13, 32, 34, 44, 47, 58, 91, 93, 123, 9, 10, 9, 10, 13, 32, 44, 47, 125,
588 | 9, 10, 13, 32, 44, 47, 125, 13, 32, 34, 44, 47, 58, 125, 9, 10, 34, 13, 32, 34, 44, 47, 58, 125, 9, 10, 42, 47, 42, 47,
589 | 13, 32, 34, 44, 47, 58, 125, 9, 10, 42, 47, 42, 47, 34, 42, 47, 42, 47, 13, 32, 34, 44, 47, 58, 91, 93, 123, 9, 10, 9,
590 | 10, 13, 32, 44, 47, 93, 9, 10, 13, 32, 44, 47, 93, 13, 32, 34, 44, 47, 58, 91, 93, 123, 9, 10, 34, 13, 32, 34, 44, 47,
591 | 58, 91, 93, 123, 9, 10, 42, 47, 42, 47, 13, 32, 34, 44, 47, 58, 91, 93, 123, 9, 10, 42, 47, 42, 47, 42, 47, 13, 32, 47,
592 | 9, 10, 13, 32, 47, 9, 10, 0};
593 | }
594 |
595 | private static final char _json_trans_keys[] = init__json_trans_keys_0();
596 |
597 | private static byte[] init__json_single_lengths_0 () {
598 | return new byte[] {0, 9, 2, 1, 2, 7, 4, 4, 2, 9, 7, 7, 7, 1, 7, 2, 2, 7, 2, 2, 1, 2, 2, 9, 7, 7, 9, 1, 9, 2, 2, 9, 2, 2, 2,
599 | 3, 3, 0, 0};
600 | }
601 |
602 | private static final byte _json_single_lengths[] = init__json_single_lengths_0();
603 |
604 | private static byte[] init__json_range_lengths_0 () {
605 | return new byte[] {0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0,
606 | 1, 1, 0, 0};
607 | }
608 |
609 | private static final byte _json_range_lengths[] = init__json_range_lengths_0();
610 |
611 | private static short[] init__json_index_offsets_0 () {
612 | return new short[] {0, 0, 11, 14, 16, 19, 28, 34, 40, 43, 54, 62, 70, 79, 81, 90, 93, 96, 105, 108, 111, 113, 116, 119, 130,
613 | 138, 146, 157, 159, 170, 173, 176, 187, 190, 193, 196, 201, 206, 207};
614 | }
615 |
616 | private static final short _json_index_offsets[] = init__json_index_offsets_0();
617 |
618 | private static byte[] init__json_indicies_0 () {
619 | return new byte[] {1, 1, 2, 3, 4, 3, 5, 3, 6, 1, 0, 7, 7, 3, 8, 3, 9, 9, 3, 11, 11, 12, 13, 14, 3, 15, 11, 10, 16, 16, 17,
620 | 18, 16, 3, 19, 19, 20, 21, 19, 3, 22, 22, 3, 21, 21, 24, 3, 25, 3, 26, 3, 27, 21, 23, 28, 29, 29, 28, 30, 31, 32, 3, 33,
621 | 34, 34, 33, 13, 35, 15, 3, 34, 34, 12, 36, 37, 3, 15, 34, 10, 16, 3, 36, 36, 12, 3, 38, 3, 3, 36, 10, 39, 39, 3, 40, 40,
622 | 3, 13, 13, 12, 3, 41, 3, 15, 13, 10, 42, 42, 3, 43, 43, 3, 28, 3, 44, 44, 3, 45, 45, 3, 47, 47, 48, 49, 50, 3, 51, 52,
623 | 53, 47, 46, 54, 55, 55, 54, 56, 57, 58, 3, 59, 60, 60, 59, 49, 61, 52, 3, 60, 60, 48, 62, 63, 3, 51, 52, 53, 60, 46, 54,
624 | 3, 62, 62, 48, 3, 64, 3, 51, 3, 53, 62, 46, 65, 65, 3, 66, 66, 3, 49, 49, 48, 3, 67, 3, 51, 52, 53, 49, 46, 68, 68, 3,
625 | 69, 69, 3, 70, 70, 3, 8, 8, 71, 8, 3, 72, 72, 73, 72, 3, 3, 3, 0};
626 | }
627 |
628 | private static final byte _json_indicies[] = init__json_indicies_0();
629 |
630 | private static byte[] init__json_trans_targs_0 () {
631 | return new byte[] {35, 1, 3, 0, 4, 36, 36, 36, 36, 1, 6, 5, 13, 17, 22, 37, 7, 8, 9, 7, 8, 9, 7, 10, 20, 21, 11, 11, 11, 12,
632 | 17, 19, 37, 11, 12, 19, 14, 16, 15, 14, 12, 18, 17, 11, 9, 5, 24, 23, 27, 31, 34, 25, 38, 25, 25, 26, 31, 33, 38, 25, 26,
633 | 33, 28, 30, 29, 28, 26, 32, 31, 25, 23, 2, 36, 2};
634 | }
635 |
636 | private static final byte _json_trans_targs[] = init__json_trans_targs_0();
637 |
638 | private static byte[] init__json_trans_actions_0 () {
639 | return new byte[] {13, 0, 15, 0, 0, 7, 3, 11, 1, 11, 17, 0, 20, 0, 0, 5, 1, 1, 1, 0, 0, 0, 11, 13, 15, 0, 7, 3, 1, 1, 1, 1,
640 | 23, 0, 0, 0, 0, 0, 0, 11, 11, 0, 11, 11, 11, 11, 13, 0, 15, 0, 0, 7, 9, 3, 1, 1, 1, 1, 26, 0, 0, 0, 0, 0, 0, 11, 11, 0,
641 | 11, 11, 11, 1, 0, 0};
642 | }
643 |
644 | private static final byte _json_trans_actions[] = init__json_trans_actions_0();
645 |
646 | private static byte[] init__json_eof_actions_0 () {
647 | return new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
648 | 1, 0, 0, 0};
649 | }
650 |
651 | private static final byte _json_eof_actions[] = init__json_eof_actions_0();
652 |
653 | static final int json_start = 1;
654 | static final int json_first_final = 35;
655 | static final int json_error = 0;
656 |
657 | static final int json_en_object = 5;
658 | static final int json_en_array = 23;
659 | static final int json_en_main = 1;
660 |
661 | // line 343 "JsonReader.rl"
662 |
663 | private final ArrayList elements = new ArrayList(8);
664 | private final ArrayList lastChild = new ArrayList(8);
665 | private JsonValue root, current;
666 |
667 | /** @param name May be null. */
668 | private void addChild (@Null String name, JsonValue child) {
669 | child.setName(name);
670 | if (current == null) {
671 | current = child;
672 | root = child;
673 | } else if (current.isArray() || current.isObject()) {
674 | child.parent = current;
675 | if (current.size == 0)
676 | current.child = child;
677 | else {
678 | JsonValue last = lastChild.remove(elements.size() - 1);
679 | last.next = child;
680 | child.prev = last;
681 | }
682 | lastChild.add(child);
683 | current.size++;
684 | } else
685 | root = current;
686 | }
687 |
688 | /** @param name May be null. */
689 | protected void startObject (@Null String name) {
690 | JsonValue value = new JsonValue(ValueType.object);
691 | if (current != null) addChild(name, value);
692 | elements.add(value);
693 | current = value;
694 | }
695 |
696 | /** @param name May be null. */
697 | protected void startArray (@Null String name) {
698 | JsonValue value = new JsonValue(ValueType.array);
699 | if (current != null) addChild(name, value);
700 | elements.add(value);
701 | current = value;
702 | }
703 |
704 | protected void pop () {
705 | root = elements.remove(elements.size() - 1);
706 | if (current.size > 0) lastChild.remove(lastChild.size() - 1);
707 | current = elements.size() > 0 ? elements.get(elements.size() - 1) : null;
708 | }
709 |
710 | protected void string (String name, String value) {
711 | addChild(name, new JsonValue(value));
712 | }
713 |
714 | protected void number (String name, double value, String stringValue) {
715 | addChild(name, new JsonValue(value, stringValue));
716 | }
717 |
718 | protected void number (String name, long value, String stringValue) {
719 | addChild(name, new JsonValue(value, stringValue));
720 | }
721 |
722 | protected void bool (String name, boolean value) {
723 | addChild(name, new JsonValue(value));
724 | }
725 |
726 | private String unescape (String value) {
727 | int length = value.length();
728 | StringBuilder buffer = new StringBuilder(length + 16);
729 | for (int i = 0; i < length;) {
730 | char c = value.charAt(i++);
731 | if (c != '\\') {
732 | buffer.append(c);
733 | continue;
734 | }
735 | if (i == length) break;
736 | c = value.charAt(i++);
737 | if (c == 'u') {
738 | buffer.append(Character.toChars(Integer.parseInt(value.substring(i, i + 4), 16)));
739 | i += 4;
740 | continue;
741 | }
742 | switch (c) {
743 | case '"':
744 | case '\\':
745 | case '/':
746 | break;
747 | case 'b':
748 | c = '\b';
749 | break;
750 | case 'f':
751 | c = '\f';
752 | break;
753 | case 'n':
754 | c = '\n';
755 | break;
756 | case 'r':
757 | c = '\r';
758 | break;
759 | case 't':
760 | c = '\t';
761 | break;
762 | default:
763 | throw new JsonException("Illegal escaped character: \\" + c);
764 | }
765 | buffer.append(c);
766 | }
767 | return buffer.toString();
768 | }
769 | }
770 |
--------------------------------------------------------------------------------
/src/com/esotericsoftware/jsonbeans/JsonSerializable.java:
--------------------------------------------------------------------------------
1 |
2 | package com.esotericsoftware.jsonbeans;
3 |
4 | public interface JsonSerializable {
5 | public void write (Json json);
6 |
7 | public void read (Json json, JsonValue jsonData);
8 | }
9 |
--------------------------------------------------------------------------------
/src/com/esotericsoftware/jsonbeans/JsonSerializer.java:
--------------------------------------------------------------------------------
1 |
2 | package com.esotericsoftware.jsonbeans;
3 |
4 | public interface JsonSerializer {
5 | public void write (Json json, T object, Class knownType);
6 |
7 | public T read (Json json, JsonValue jsonData, Class type);
8 | }
9 |
--------------------------------------------------------------------------------
/src/com/esotericsoftware/jsonbeans/JsonValue.java:
--------------------------------------------------------------------------------
1 |
2 | package com.esotericsoftware.jsonbeans;
3 |
4 | import java.io.IOException;
5 | import java.io.Writer;
6 | import java.util.Iterator;
7 | import java.util.NoSuchElementException;
8 |
9 | /** Container for a JSON object, array, string, double, long, boolean, or null.
10 | *
11 | * JsonValue children are a linked list. Iteration of arrays or objects is easily done using a for loop, either with the enhanced
12 | * for loop syntactic sugar or like the example below. This is much more efficient than accessing children by index when there are
13 | * many children.
14 | *
15 | *
20 | *
21 | * @author Nathan Sweet */
22 | public class JsonValue implements Iterable {
23 | private ValueType type;
24 |
25 | /** May be null. */
26 | private String stringValue;
27 | private double doubleValue;
28 | private long longValue;
29 |
30 | public String name;
31 | /** May be null. */
32 | public JsonValue child, parent;
33 | /** May be null. When changing this field the parent {@link #size()} may need to be changed. */
34 | public JsonValue next, prev;
35 | public int size;
36 |
37 | public JsonValue (ValueType type) {
38 | this.type = type;
39 | }
40 |
41 | /** @param value May be null. */
42 | public JsonValue (@Null String value) {
43 | set(value);
44 | }
45 |
46 | public JsonValue (double value) {
47 | set(value, null);
48 | }
49 |
50 | public JsonValue (long value) {
51 | set(value, null);
52 | }
53 |
54 | public JsonValue (double value, String stringValue) {
55 | set(value, stringValue);
56 | }
57 |
58 | public JsonValue (long value, String stringValue) {
59 | set(value, stringValue);
60 | }
61 |
62 | public JsonValue (boolean value) {
63 | set(value);
64 | }
65 |
66 | /** Returns the child at the specified index. This requires walking the linked list to the specified entry, see
67 | * {@link JsonValue} for how to iterate efficiently.
68 | * @return May be null. */
69 | public @Null JsonValue get (int index) {
70 | JsonValue current = child;
71 | while (current != null && index > 0) {
72 | index--;
73 | current = current.next;
74 | }
75 | return current;
76 | }
77 |
78 | /** Returns the child with the specified name.
79 | * @return May be null. */
80 | public @Null JsonValue get (String name) {
81 | JsonValue current = child;
82 | while (current != null && (current.name == null || !current.name.equalsIgnoreCase(name)))
83 | current = current.next;
84 | return current;
85 | }
86 |
87 | /** Returns true if a child with the specified name exists. */
88 | public boolean has (String name) {
89 | return get(name) != null;
90 | }
91 |
92 | /** Returns the child at the specified index. This requires walking the linked list to the specified entry, see
93 | * {@link JsonValue} for how to iterate efficiently.
94 | * @throws IllegalArgumentException if the child was not found. */
95 | public JsonValue require (int index) {
96 | JsonValue current = child;
97 | while (current != null && index > 0) {
98 | index--;
99 | current = current.next;
100 | }
101 | if (current == null) throw new IllegalArgumentException("Child not found with index: " + index);
102 | return current;
103 | }
104 |
105 | /** Returns the child with the specified name.
106 | * @throws IllegalArgumentException if the child was not found. */
107 | public JsonValue require (String name) {
108 | JsonValue current = child;
109 | while (current != null && (current.name == null || !current.name.equalsIgnoreCase(name)))
110 | current = current.next;
111 | if (current == null) throw new IllegalArgumentException("Child not found with name: " + name);
112 | return current;
113 | }
114 |
115 | /** Removes the child with the specified index. This requires walking the linked list to the specified entry, see
116 | * {@link JsonValue} for how to iterate efficiently.
117 | * @return May be null. */
118 | public @Null JsonValue remove (int index) {
119 | JsonValue child = get(index);
120 | if (child == null) return null;
121 | if (child.prev == null) {
122 | this.child = child.next;
123 | if (this.child != null) this.child.prev = null;
124 | } else {
125 | child.prev.next = child.next;
126 | if (child.next != null) child.next.prev = child.prev;
127 | }
128 | size--;
129 | return child;
130 | }
131 |
132 | /** Removes the child with the specified name.
133 | * @return May be null. */
134 | public @Null JsonValue remove (String name) {
135 | JsonValue child = get(name);
136 | if (child == null) return null;
137 | if (child.prev == null) {
138 | this.child = child.next;
139 | if (this.child != null) this.child.prev = null;
140 | } else {
141 | child.prev.next = child.next;
142 | if (child.next != null) child.next.prev = child.prev;
143 | }
144 | size--;
145 | return child;
146 | }
147 |
148 | /** Removes this value from its parent. */
149 | public void remove () {
150 | if (parent == null) throw new IllegalStateException();
151 | if (prev == null) {
152 | parent.child = next;
153 | if (parent.child != null) parent.child.prev = null;
154 | } else {
155 | prev.next = next;
156 | if (next != null) next.prev = prev;
157 | }
158 | parent.size--;
159 | }
160 |
161 | /** Returns true if there are one or more children in the array or object. */
162 | public boolean notEmpty () {
163 | return size > 0;
164 | }
165 |
166 | /** Returns true if there are not children in the array or object. */
167 | public boolean isEmpty () {
168 | return size == 0;
169 | }
170 |
171 | /** @deprecated Use {@link #size} instead. Returns this number of children in the array or object. */
172 | @Deprecated
173 | public int size () {
174 | return size;
175 | }
176 |
177 | /** Returns this value as a string.
178 | * @return May be null if this value is null.
179 | * @throws IllegalStateException if this an array or object. */
180 | public @Null String asString () {
181 | switch (type) {
182 | case stringValue:
183 | return stringValue;
184 | case doubleValue:
185 | return stringValue != null ? stringValue : Double.toString(doubleValue);
186 | case longValue:
187 | return stringValue != null ? stringValue : Long.toString(longValue);
188 | case booleanValue:
189 | return longValue != 0 ? "true" : "false";
190 | case nullValue:
191 | return null;
192 | }
193 | throw new IllegalStateException("Value cannot be converted to string: " + type);
194 | }
195 |
196 | /** Returns this value as a float.
197 | * @throws IllegalStateException if this an array or object. */
198 | public float asFloat () {
199 | switch (type) {
200 | case stringValue:
201 | return Float.parseFloat(stringValue);
202 | case doubleValue:
203 | return (float)doubleValue;
204 | case longValue:
205 | return longValue;
206 | case booleanValue:
207 | return longValue != 0 ? 1 : 0;
208 | }
209 | throw new IllegalStateException("Value cannot be converted to float: " + type);
210 | }
211 |
212 | /** Returns this value as a double.
213 | * @throws IllegalStateException if this an array or object. */
214 | public double asDouble () {
215 | switch (type) {
216 | case stringValue:
217 | return Double.parseDouble(stringValue);
218 | case doubleValue:
219 | return doubleValue;
220 | case longValue:
221 | return longValue;
222 | case booleanValue:
223 | return longValue != 0 ? 1 : 0;
224 | }
225 | throw new IllegalStateException("Value cannot be converted to double: " + type);
226 | }
227 |
228 | /** Returns this value as a long.
229 | * @throws IllegalStateException if this an array or object. */
230 | public long asLong () {
231 | switch (type) {
232 | case stringValue:
233 | return Long.parseLong(stringValue);
234 | case doubleValue:
235 | return (long)doubleValue;
236 | case longValue:
237 | return longValue;
238 | case booleanValue:
239 | return longValue != 0 ? 1 : 0;
240 | }
241 | throw new IllegalStateException("Value cannot be converted to long: " + type);
242 | }
243 |
244 | /** Returns this value as an int.
245 | * @throws IllegalStateException if this an array or object. */
246 | public int asInt () {
247 | switch (type) {
248 | case stringValue:
249 | return Integer.parseInt(stringValue);
250 | case doubleValue:
251 | return (int)doubleValue;
252 | case longValue:
253 | return (int)longValue;
254 | case booleanValue:
255 | return longValue != 0 ? 1 : 0;
256 | }
257 | throw new IllegalStateException("Value cannot be converted to int: " + type);
258 | }
259 |
260 | /** Returns this value as a boolean.
261 | * @throws IllegalStateException if this an array or object. */
262 | public boolean asBoolean () {
263 | switch (type) {
264 | case stringValue:
265 | return stringValue.equalsIgnoreCase("true");
266 | case doubleValue:
267 | return doubleValue != 0;
268 | case longValue:
269 | return longValue != 0;
270 | case booleanValue:
271 | return longValue != 0;
272 | }
273 | throw new IllegalStateException("Value cannot be converted to boolean: " + type);
274 | }
275 |
276 | /** Returns this value as a byte.
277 | * @throws IllegalStateException if this an array or object. */
278 | public byte asByte () {
279 | switch (type) {
280 | case stringValue:
281 | return Byte.parseByte(stringValue);
282 | case doubleValue:
283 | return (byte)doubleValue;
284 | case longValue:
285 | return (byte)longValue;
286 | case booleanValue:
287 | return longValue != 0 ? (byte)1 : 0;
288 | }
289 | throw new IllegalStateException("Value cannot be converted to byte: " + type);
290 | }
291 |
292 | /** Returns this value as a short.
293 | * @throws IllegalStateException if this an array or object. */
294 | public short asShort () {
295 | switch (type) {
296 | case stringValue:
297 | return Short.parseShort(stringValue);
298 | case doubleValue:
299 | return (short)doubleValue;
300 | case longValue:
301 | return (short)longValue;
302 | case booleanValue:
303 | return longValue != 0 ? (short)1 : 0;
304 | }
305 | throw new IllegalStateException("Value cannot be converted to short: " + type);
306 | }
307 |
308 | /** Returns this value as a char.
309 | * @throws IllegalStateException if this an array or object. */
310 | public char asChar () {
311 | switch (type) {
312 | case stringValue:
313 | return stringValue.length() == 0 ? 0 : stringValue.charAt(0);
314 | case doubleValue:
315 | return (char)doubleValue;
316 | case longValue:
317 | return (char)longValue;
318 | case booleanValue:
319 | return longValue != 0 ? (char)1 : 0;
320 | }
321 | throw new IllegalStateException("Value cannot be converted to char: " + type);
322 | }
323 |
324 | /** Returns the children of this value as a newly allocated String array.
325 | * @throws IllegalStateException if this is not an array. */
326 | public String[] asStringArray () {
327 | if (type != ValueType.array) throw new IllegalStateException("Value is not an array: " + type);
328 | String[] array = new String[size];
329 | int i = 0;
330 | for (JsonValue value = child; value != null; value = value.next, i++) {
331 | String v;
332 | switch (value.type) {
333 | case stringValue:
334 | v = value.stringValue;
335 | break;
336 | case doubleValue:
337 | v = stringValue != null ? stringValue : Double.toString(value.doubleValue);
338 | break;
339 | case longValue:
340 | v = stringValue != null ? stringValue : Long.toString(value.longValue);
341 | break;
342 | case booleanValue:
343 | v = value.longValue != 0 ? "true" : "false";
344 | break;
345 | case nullValue:
346 | v = null;
347 | break;
348 | default:
349 | throw new IllegalStateException("Value cannot be converted to string: " + value.type);
350 | }
351 | array[i] = v;
352 | }
353 | return array;
354 | }
355 |
356 | /** Returns the children of this value as a newly allocated float array.
357 | * @throws IllegalStateException if this is not an array. */
358 | public float[] asFloatArray () {
359 | if (type != ValueType.array) throw new IllegalStateException("Value is not an array: " + type);
360 | float[] array = new float[size];
361 | int i = 0;
362 | for (JsonValue value = child; value != null; value = value.next, i++) {
363 | float v;
364 | switch (value.type) {
365 | case stringValue:
366 | v = Float.parseFloat(value.stringValue);
367 | break;
368 | case doubleValue:
369 | v = (float)value.doubleValue;
370 | break;
371 | case longValue:
372 | v = value.longValue;
373 | break;
374 | case booleanValue:
375 | v = value.longValue != 0 ? 1 : 0;
376 | break;
377 | default:
378 | throw new IllegalStateException("Value cannot be converted to float: " + value.type);
379 | }
380 | array[i] = v;
381 | }
382 | return array;
383 | }
384 |
385 | /** Returns the children of this value as a newly allocated double array.
386 | * @throws IllegalStateException if this is not an array. */
387 | public double[] asDoubleArray () {
388 | if (type != ValueType.array) throw new IllegalStateException("Value is not an array: " + type);
389 | double[] array = new double[size];
390 | int i = 0;
391 | for (JsonValue value = child; value != null; value = value.next, i++) {
392 | double v;
393 | switch (value.type) {
394 | case stringValue:
395 | v = Double.parseDouble(value.stringValue);
396 | break;
397 | case doubleValue:
398 | v = value.doubleValue;
399 | break;
400 | case longValue:
401 | v = value.longValue;
402 | break;
403 | case booleanValue:
404 | v = value.longValue != 0 ? 1 : 0;
405 | break;
406 | default:
407 | throw new IllegalStateException("Value cannot be converted to double: " + value.type);
408 | }
409 | array[i] = v;
410 | }
411 | return array;
412 | }
413 |
414 | /** Returns the children of this value as a newly allocated long array.
415 | * @throws IllegalStateException if this is not an array. */
416 | public long[] asLongArray () {
417 | if (type != ValueType.array) throw new IllegalStateException("Value is not an array: " + type);
418 | long[] array = new long[size];
419 | int i = 0;
420 | for (JsonValue value = child; value != null; value = value.next, i++) {
421 | long v;
422 | switch (value.type) {
423 | case stringValue:
424 | v = Long.parseLong(value.stringValue);
425 | break;
426 | case doubleValue:
427 | v = (long)value.doubleValue;
428 | break;
429 | case longValue:
430 | v = value.longValue;
431 | break;
432 | case booleanValue:
433 | v = value.longValue != 0 ? 1 : 0;
434 | break;
435 | default:
436 | throw new IllegalStateException("Value cannot be converted to long: " + value.type);
437 | }
438 | array[i] = v;
439 | }
440 | return array;
441 | }
442 |
443 | /** Returns the children of this value as a newly allocated int array.
444 | * @throws IllegalStateException if this is not an array. */
445 | public int[] asIntArray () {
446 | if (type != ValueType.array) throw new IllegalStateException("Value is not an array: " + type);
447 | int[] array = new int[size];
448 | int i = 0;
449 | for (JsonValue value = child; value != null; value = value.next, i++) {
450 | int v;
451 | switch (value.type) {
452 | case stringValue:
453 | v = Integer.parseInt(value.stringValue);
454 | break;
455 | case doubleValue:
456 | v = (int)value.doubleValue;
457 | break;
458 | case longValue:
459 | v = (int)value.longValue;
460 | break;
461 | case booleanValue:
462 | v = value.longValue != 0 ? 1 : 0;
463 | break;
464 | default:
465 | throw new IllegalStateException("Value cannot be converted to int: " + value.type);
466 | }
467 | array[i] = v;
468 | }
469 | return array;
470 | }
471 |
472 | /** Returns the children of this value as a newly allocated boolean array.
473 | * @throws IllegalStateException if this is not an array. */
474 | public boolean[] asBooleanArray () {
475 | if (type != ValueType.array) throw new IllegalStateException("Value is not an array: " + type);
476 | boolean[] array = new boolean[size];
477 | int i = 0;
478 | for (JsonValue value = child; value != null; value = value.next, i++) {
479 | boolean v;
480 | switch (value.type) {
481 | case stringValue:
482 | v = Boolean.parseBoolean(value.stringValue);
483 | break;
484 | case doubleValue:
485 | v = value.doubleValue == 0;
486 | break;
487 | case longValue:
488 | v = value.longValue == 0;
489 | break;
490 | case booleanValue:
491 | v = value.longValue != 0;
492 | break;
493 | default:
494 | throw new IllegalStateException("Value cannot be converted to boolean: " + value.type);
495 | }
496 | array[i] = v;
497 | }
498 | return array;
499 | }
500 |
501 | /** Returns the children of this value as a newly allocated byte array.
502 | * @throws IllegalStateException if this is not an array. */
503 | public byte[] asByteArray () {
504 | if (type != ValueType.array) throw new IllegalStateException("Value is not an array: " + type);
505 | byte[] array = new byte[size];
506 | int i = 0;
507 | for (JsonValue value = child; value != null; value = value.next, i++) {
508 | byte v;
509 | switch (value.type) {
510 | case stringValue:
511 | v = Byte.parseByte(value.stringValue);
512 | break;
513 | case doubleValue:
514 | v = (byte)value.doubleValue;
515 | break;
516 | case longValue:
517 | v = (byte)value.longValue;
518 | break;
519 | case booleanValue:
520 | v = value.longValue != 0 ? (byte)1 : 0;
521 | break;
522 | default:
523 | throw new IllegalStateException("Value cannot be converted to byte: " + value.type);
524 | }
525 | array[i] = v;
526 | }
527 | return array;
528 | }
529 |
530 | /** Returns the children of this value as a newly allocated short array.
531 | * @throws IllegalStateException if this is not an array. */
532 | public short[] asShortArray () {
533 | if (type != ValueType.array) throw new IllegalStateException("Value is not an array: " + type);
534 | short[] array = new short[size];
535 | int i = 0;
536 | for (JsonValue value = child; value != null; value = value.next, i++) {
537 | short v;
538 | switch (value.type) {
539 | case stringValue:
540 | v = Short.parseShort(value.stringValue);
541 | break;
542 | case doubleValue:
543 | v = (short)value.doubleValue;
544 | break;
545 | case longValue:
546 | v = (short)value.longValue;
547 | break;
548 | case booleanValue:
549 | v = value.longValue != 0 ? (short)1 : 0;
550 | break;
551 | default:
552 | throw new IllegalStateException("Value cannot be converted to short: " + value.type);
553 | }
554 | array[i] = v;
555 | }
556 | return array;
557 | }
558 |
559 | /** Returns the children of this value as a newly allocated char array.
560 | * @throws IllegalStateException if this is not an array. */
561 | public char[] asCharArray () {
562 | if (type != ValueType.array) throw new IllegalStateException("Value is not an array: " + type);
563 | char[] array = new char[size];
564 | int i = 0;
565 | for (JsonValue value = child; value != null; value = value.next, i++) {
566 | char v;
567 | switch (value.type) {
568 | case stringValue:
569 | v = value.stringValue.length() == 0 ? 0 : value.stringValue.charAt(0);
570 | break;
571 | case doubleValue:
572 | v = (char)value.doubleValue;
573 | break;
574 | case longValue:
575 | v = (char)value.longValue;
576 | break;
577 | case booleanValue:
578 | v = value.longValue != 0 ? (char)1 : 0;
579 | break;
580 | default:
581 | throw new IllegalStateException("Value cannot be converted to char: " + value.type);
582 | }
583 | array[i] = v;
584 | }
585 | return array;
586 | }
587 |
588 | /** Returns true if a child with the specified name exists and has a child. */
589 | public boolean hasChild (String name) {
590 | return getChild(name) != null;
591 | }
592 |
593 | /** Finds the child with the specified name and returns its first child.
594 | * @return May be null. */
595 | public @Null JsonValue getChild (String name) {
596 | JsonValue child = get(name);
597 | return child == null ? null : child.child;
598 | }
599 |
600 | /** Finds the child with the specified name and returns it as a string. Returns defaultValue if not found.
601 | * @param defaultValue May be null. */
602 | public String getString (String name, @Null String defaultValue) {
603 | JsonValue child = get(name);
604 | return (child == null || !child.isValue() || child.isNull()) ? defaultValue : child.asString();
605 | }
606 |
607 | /** Finds the child with the specified name and returns it as a float. Returns defaultValue if not found. */
608 | public float getFloat (String name, float defaultValue) {
609 | JsonValue child = get(name);
610 | return (child == null || !child.isValue() || child.isNull()) ? defaultValue : child.asFloat();
611 | }
612 |
613 | /** Finds the child with the specified name and returns it as a double. Returns defaultValue if not found. */
614 | public double getDouble (String name, double defaultValue) {
615 | JsonValue child = get(name);
616 | return (child == null || !child.isValue() || child.isNull()) ? defaultValue : child.asDouble();
617 | }
618 |
619 | /** Finds the child with the specified name and returns it as a long. Returns defaultValue if not found. */
620 | public long getLong (String name, long defaultValue) {
621 | JsonValue child = get(name);
622 | return (child == null || !child.isValue() || child.isNull()) ? defaultValue : child.asLong();
623 | }
624 |
625 | /** Finds the child with the specified name and returns it as an int. Returns defaultValue if not found. */
626 | public int getInt (String name, int defaultValue) {
627 | JsonValue child = get(name);
628 | return (child == null || !child.isValue() || child.isNull()) ? defaultValue : child.asInt();
629 | }
630 |
631 | /** Finds the child with the specified name and returns it as a boolean. Returns defaultValue if not found. */
632 | public boolean getBoolean (String name, boolean defaultValue) {
633 | JsonValue child = get(name);
634 | return (child == null || !child.isValue() || child.isNull()) ? defaultValue : child.asBoolean();
635 | }
636 |
637 | /** Finds the child with the specified name and returns it as a byte. Returns defaultValue if not found. */
638 | public byte getByte (String name, byte defaultValue) {
639 | JsonValue child = get(name);
640 | return (child == null || !child.isValue() || child.isNull()) ? defaultValue : child.asByte();
641 | }
642 |
643 | /** Finds the child with the specified name and returns it as a short. Returns defaultValue if not found. */
644 | public short getShort (String name, short defaultValue) {
645 | JsonValue child = get(name);
646 | return (child == null || !child.isValue() || child.isNull()) ? defaultValue : child.asShort();
647 | }
648 |
649 | /** Finds the child with the specified name and returns it as a char. Returns defaultValue if not found. */
650 | public char getChar (String name, char defaultValue) {
651 | JsonValue child = get(name);
652 | return (child == null || !child.isValue() || child.isNull()) ? defaultValue : child.asChar();
653 | }
654 |
655 | /** Finds the child with the specified name and returns it as a string.
656 | * @throws IllegalArgumentException if the child was not found. */
657 | public String getString (String name) {
658 | JsonValue child = get(name);
659 | if (child == null) throw new IllegalArgumentException("Named value not found: " + name);
660 | return child.asString();
661 | }
662 |
663 | /** Finds the child with the specified name and returns it as a float.
664 | * @throws IllegalArgumentException if the child was not found. */
665 | public float getFloat (String name) {
666 | JsonValue child = get(name);
667 | if (child == null) throw new IllegalArgumentException("Named value not found: " + name);
668 | return child.asFloat();
669 | }
670 |
671 | /** Finds the child with the specified name and returns it as a double.
672 | * @throws IllegalArgumentException if the child was not found. */
673 | public double getDouble (String name) {
674 | JsonValue child = get(name);
675 | if (child == null) throw new IllegalArgumentException("Named value not found: " + name);
676 | return child.asDouble();
677 | }
678 |
679 | /** Finds the child with the specified name and returns it as a long.
680 | * @throws IllegalArgumentException if the child was not found. */
681 | public long getLong (String name) {
682 | JsonValue child = get(name);
683 | if (child == null) throw new IllegalArgumentException("Named value not found: " + name);
684 | return child.asLong();
685 | }
686 |
687 | /** Finds the child with the specified name and returns it as an int.
688 | * @throws IllegalArgumentException if the child was not found. */
689 | public int getInt (String name) {
690 | JsonValue child = get(name);
691 | if (child == null) throw new IllegalArgumentException("Named value not found: " + name);
692 | return child.asInt();
693 | }
694 |
695 | /** Finds the child with the specified name and returns it as a boolean.
696 | * @throws IllegalArgumentException if the child was not found. */
697 | public boolean getBoolean (String name) {
698 | JsonValue child = get(name);
699 | if (child == null) throw new IllegalArgumentException("Named value not found: " + name);
700 | return child.asBoolean();
701 | }
702 |
703 | /** Finds the child with the specified name and returns it as a byte.
704 | * @throws IllegalArgumentException if the child was not found. */
705 | public byte getByte (String name) {
706 | JsonValue child = get(name);
707 | if (child == null) throw new IllegalArgumentException("Named value not found: " + name);
708 | return child.asByte();
709 | }
710 |
711 | /** Finds the child with the specified name and returns it as a short.
712 | * @throws IllegalArgumentException if the child was not found. */
713 | public short getShort (String name) {
714 | JsonValue child = get(name);
715 | if (child == null) throw new IllegalArgumentException("Named value not found: " + name);
716 | return child.asShort();
717 | }
718 |
719 | /** Finds the child with the specified name and returns it as a char.
720 | * @throws IllegalArgumentException if the child was not found. */
721 | public char getChar (String name) {
722 | JsonValue child = get(name);
723 | if (child == null) throw new IllegalArgumentException("Named value not found: " + name);
724 | return child.asChar();
725 | }
726 |
727 | /** Finds the child with the specified index and returns it as a string.
728 | * @throws IllegalArgumentException if the child was not found. */
729 | public String getString (int index) {
730 | JsonValue child = get(index);
731 | if (child == null) throw new IllegalArgumentException("Indexed value not found: " + name);
732 | return child.asString();
733 | }
734 |
735 | /** Finds the child with the specified index and returns it as a float.
736 | * @throws IllegalArgumentException if the child was not found. */
737 | public float getFloat (int index) {
738 | JsonValue child = get(index);
739 | if (child == null) throw new IllegalArgumentException("Indexed value not found: " + name);
740 | return child.asFloat();
741 | }
742 |
743 | /** Finds the child with the specified index and returns it as a double.
744 | * @throws IllegalArgumentException if the child was not found. */
745 | public double getDouble (int index) {
746 | JsonValue child = get(index);
747 | if (child == null) throw new IllegalArgumentException("Indexed value not found: " + name);
748 | return child.asDouble();
749 | }
750 |
751 | /** Finds the child with the specified index and returns it as a long.
752 | * @throws IllegalArgumentException if the child was not found. */
753 | public long getLong (int index) {
754 | JsonValue child = get(index);
755 | if (child == null) throw new IllegalArgumentException("Indexed value not found: " + name);
756 | return child.asLong();
757 | }
758 |
759 | /** Finds the child with the specified index and returns it as an int.
760 | * @throws IllegalArgumentException if the child was not found. */
761 | public int getInt (int index) {
762 | JsonValue child = get(index);
763 | if (child == null) throw new IllegalArgumentException("Indexed value not found: " + name);
764 | return child.asInt();
765 | }
766 |
767 | /** Finds the child with the specified index and returns it as a boolean.
768 | * @throws IllegalArgumentException if the child was not found. */
769 | public boolean getBoolean (int index) {
770 | JsonValue child = get(index);
771 | if (child == null) throw new IllegalArgumentException("Indexed value not found: " + name);
772 | return child.asBoolean();
773 | }
774 |
775 | /** Finds the child with the specified index and returns it as a byte.
776 | * @throws IllegalArgumentException if the child was not found. */
777 | public byte getByte (int index) {
778 | JsonValue child = get(index);
779 | if (child == null) throw new IllegalArgumentException("Indexed value not found: " + name);
780 | return child.asByte();
781 | }
782 |
783 | /** Finds the child with the specified index and returns it as a short.
784 | * @throws IllegalArgumentException if the child was not found. */
785 | public short getShort (int index) {
786 | JsonValue child = get(index);
787 | if (child == null) throw new IllegalArgumentException("Indexed value not found: " + name);
788 | return child.asShort();
789 | }
790 |
791 | /** Finds the child with the specified index and returns it as a char.
792 | * @throws IllegalArgumentException if the child was not found. */
793 | public char getChar (int index) {
794 | JsonValue child = get(index);
795 | if (child == null) throw new IllegalArgumentException("Indexed value not found: " + name);
796 | return child.asChar();
797 | }
798 |
799 | public ValueType type () {
800 | return type;
801 | }
802 |
803 | public void setType (ValueType type) {
804 | if (type == null) throw new IllegalArgumentException("type cannot be null.");
805 | this.type = type;
806 | }
807 |
808 | public boolean isArray () {
809 | return type == ValueType.array;
810 | }
811 |
812 | public boolean isObject () {
813 | return type == ValueType.object;
814 | }
815 |
816 | public boolean isString () {
817 | return type == ValueType.stringValue;
818 | }
819 |
820 | /** Returns true if this is a double or long value. */
821 | public boolean isNumber () {
822 | return type == ValueType.doubleValue || type == ValueType.longValue;
823 | }
824 |
825 | public boolean isDouble () {
826 | return type == ValueType.doubleValue;
827 | }
828 |
829 | public boolean isLong () {
830 | return type == ValueType.longValue;
831 | }
832 |
833 | public boolean isBoolean () {
834 | return type == ValueType.booleanValue;
835 | }
836 |
837 | public boolean isNull () {
838 | return type == ValueType.nullValue;
839 | }
840 |
841 | /** Returns true if this is not an array or object. */
842 | public boolean isValue () {
843 | switch (type) {
844 | case stringValue:
845 | case doubleValue:
846 | case longValue:
847 | case booleanValue:
848 | case nullValue:
849 | return true;
850 | }
851 | return false;
852 | }
853 |
854 | /** Returns the name for this object value.
855 | * @return May be null. */
856 | public @Null String name () {
857 | return name;
858 | }
859 |
860 | /** @param name May be null. */
861 | public void setName (@Null String name) {
862 | this.name = name;
863 | }
864 |
865 | /** Returns the parent for this value.
866 | * @return May be null. */
867 | public @Null JsonValue parent () {
868 | return parent;
869 | }
870 |
871 | /** Returns the first child for this object or array.
872 | * @return May be null. */
873 | public @Null JsonValue child () {
874 | return child;
875 | }
876 |
877 | /** Sets the name of the specified value and adds it after the last child. */
878 | public void addChild (String name, JsonValue value) {
879 | if (name == null) throw new IllegalArgumentException("name cannot be null.");
880 | value.name = name;
881 | addChild(value);
882 | }
883 |
884 | /** Adds the specified value after the last child. */
885 | public void addChild (JsonValue value) {
886 | value.parent = this;
887 | size++;
888 | JsonValue current = child;
889 | if (current == null)
890 | child = value;
891 | else {
892 | while (true) {
893 | if (current.next == null) {
894 | current.next = value;
895 | value.prev = current;
896 | return;
897 | }
898 | current = current.next;
899 | }
900 | }
901 | }
902 |
903 | /** Returns the next sibling of this value.
904 | * @return May be null. */
905 | public @Null JsonValue next () {
906 | return next;
907 | }
908 |
909 | /** Sets the next sibling of this value. Does not change the parent {@link #size()}.
910 | * @param next May be null. */
911 | public void setNext (@Null JsonValue next) {
912 | this.next = next;
913 | }
914 |
915 | /** Returns the previous sibling of this value.
916 | * @return May be null. */
917 | public @Null JsonValue prev () {
918 | return prev;
919 | }
920 |
921 | /** Sets the next sibling of this value. Does not change the parent {@link #size()}.
922 | * @param prev May be null. */
923 | public void setPrev (@Null JsonValue prev) {
924 | this.prev = prev;
925 | }
926 |
927 | /** @param value May be null. */
928 | public void set (@Null String value) {
929 | stringValue = value;
930 | type = value == null ? ValueType.nullValue : ValueType.stringValue;
931 | }
932 |
933 | /** @param stringValue May be null if the string representation is the string value of the double (eg, no leading zeros). */
934 | public void set (double value, @Null String stringValue) {
935 | doubleValue = value;
936 | longValue = (long)value;
937 | this.stringValue = stringValue;
938 | type = ValueType.doubleValue;
939 | }
940 |
941 | /** @param stringValue May be null if the string representation is the string value of the long (eg, no leading zeros). */
942 | public void set (long value, @Null String stringValue) {
943 | longValue = value;
944 | doubleValue = value;
945 | this.stringValue = stringValue;
946 | type = ValueType.longValue;
947 | }
948 |
949 | public void set (boolean value) {
950 | longValue = value ? 1 : 0;
951 | type = ValueType.booleanValue;
952 | }
953 |
954 | public String toJson (OutputType outputType) {
955 | if (isValue()) return asString();
956 | StringBuilder buffer = new StringBuilder(512);
957 | json(this, buffer, outputType);
958 | return buffer.toString();
959 | }
960 |
961 | private void json (JsonValue object, StringBuilder buffer, OutputType outputType) {
962 | if (object.isObject()) {
963 | if (object.child == null)
964 | buffer.append("{}");
965 | else {
966 | int start = buffer.length();
967 | while (true) {
968 | buffer.append('{');
969 | int i = 0;
970 | for (JsonValue child = object.child; child != null; child = child.next) {
971 | buffer.append(outputType.quoteName(child.name));
972 | buffer.append(':');
973 | json(child, buffer, outputType);
974 | if (child.next != null) buffer.append(',');
975 | }
976 | break;
977 | }
978 | buffer.append('}');
979 | }
980 | } else if (object.isArray()) {
981 | if (object.child == null)
982 | buffer.append("[]");
983 | else {
984 | int start = buffer.length();
985 | while (true) {
986 | buffer.append('[');
987 | for (JsonValue child = object.child; child != null; child = child.next) {
988 | json(child, buffer, outputType);
989 | if (child.next != null) buffer.append(',');
990 | }
991 | break;
992 | }
993 | buffer.append(']');
994 | }
995 | } else if (object.isString()) {
996 | buffer.append(outputType.quoteValue(object.asString()));
997 | } else if (object.isDouble()) {
998 | double doubleValue = object.asDouble();
999 | long longValue = object.asLong();
1000 | buffer.append(doubleValue == longValue ? longValue : doubleValue);
1001 | } else if (object.isLong()) {
1002 | buffer.append(object.asLong());
1003 | } else if (object.isBoolean()) {
1004 | buffer.append(object.asBoolean());
1005 | } else if (object.isNull()) {
1006 | buffer.append("null");
1007 | } else
1008 | throw new JsonException("Unknown object type: " + object);
1009 | }
1010 |
1011 | public JsonIterator iterator () {
1012 | return new JsonIterator();
1013 | }
1014 |
1015 | public String toString () {
1016 | if (isValue()) return name == null ? asString() : name + ": " + asString();
1017 | return (name == null ? "" : name + ": ") + prettyPrint(OutputType.minimal, 0);
1018 | }
1019 |
1020 | /** Returns a human readable string representing the path from the root of the JSON object graph to this value. */
1021 | public String trace () {
1022 | if (parent == null) {
1023 | if (type == ValueType.array) return "[]";
1024 | if (type == ValueType.object) return "{}";
1025 | return "";
1026 | }
1027 | String trace;
1028 | if (parent.type == ValueType.array) {
1029 | trace = "[]";
1030 | int i = 0;
1031 | for (JsonValue child = parent.child; child != null; child = child.next, i++) {
1032 | if (child == this) {
1033 | trace = "[" + i + "]";
1034 | break;
1035 | }
1036 | }
1037 | } else if (name.indexOf('.') != -1)
1038 | trace = ".\"" + name.replace("\"", "\\\"") + "\"";
1039 | else
1040 | trace = '.' + name;
1041 | return parent.trace() + trace;
1042 | }
1043 |
1044 | public String prettyPrint (OutputType outputType, int singleLineColumns) {
1045 | PrettyPrintSettings settings = new PrettyPrintSettings();
1046 | settings.outputType = outputType;
1047 | settings.singleLineColumns = singleLineColumns;
1048 | return prettyPrint(settings);
1049 | }
1050 |
1051 | public String prettyPrint (PrettyPrintSettings settings) {
1052 | StringBuilder buffer = new StringBuilder(512);
1053 | prettyPrint(this, buffer, 0, settings);
1054 | return buffer.toString();
1055 | }
1056 |
1057 | private void prettyPrint (JsonValue object, StringBuilder buffer, int indent, PrettyPrintSettings settings) {
1058 | OutputType outputType = settings.outputType;
1059 | if (object.isObject()) {
1060 | if (object.child == null)
1061 | buffer.append("{}");
1062 | else {
1063 | boolean newLines = !isFlat(object);
1064 | int start = buffer.length();
1065 | outer:
1066 | while (true) {
1067 | buffer.append(newLines ? "{\n" : "{ ");
1068 | int i = 0;
1069 | for (JsonValue child = object.child; child != null; child = child.next) {
1070 | if (newLines) indent(indent, buffer);
1071 | buffer.append(outputType.quoteName(child.name));
1072 | buffer.append(": ");
1073 | prettyPrint(child, buffer, indent + 1, settings);
1074 | if ((!newLines || outputType != OutputType.minimal) && child.next != null) buffer.append(',');
1075 | buffer.append(newLines ? '\n' : ' ');
1076 | if (!newLines && buffer.length() - start > settings.singleLineColumns) {
1077 | buffer.setLength(start);
1078 | newLines = true;
1079 | continue outer;
1080 | }
1081 | }
1082 | break;
1083 | }
1084 | if (newLines) indent(indent - 1, buffer);
1085 | buffer.append('}');
1086 | }
1087 | } else if (object.isArray()) {
1088 | if (object.child == null)
1089 | buffer.append("[]");
1090 | else {
1091 | boolean newLines = !isFlat(object);
1092 | boolean wrap = settings.wrapNumericArrays || !isNumeric(object);
1093 | int start = buffer.length();
1094 | outer:
1095 | while (true) {
1096 | buffer.append(newLines ? "[\n" : "[ ");
1097 | for (JsonValue child = object.child; child != null; child = child.next) {
1098 | if (newLines) indent(indent, buffer);
1099 | prettyPrint(child, buffer, indent + 1, settings);
1100 | if ((!newLines || outputType != OutputType.minimal) && child.next != null) buffer.append(',');
1101 | buffer.append(newLines ? '\n' : ' ');
1102 | if (wrap && !newLines && buffer.length() - start > settings.singleLineColumns) {
1103 | buffer.setLength(start);
1104 | newLines = true;
1105 | continue outer;
1106 | }
1107 | }
1108 | break;
1109 | }
1110 | if (newLines) indent(indent - 1, buffer);
1111 | buffer.append(']');
1112 | }
1113 | } else if (object.isString()) {
1114 | buffer.append(outputType.quoteValue(object.asString()));
1115 | } else if (object.isDouble()) {
1116 | double doubleValue = object.asDouble();
1117 | long longValue = object.asLong();
1118 | buffer.append(doubleValue == longValue ? longValue : doubleValue);
1119 | } else if (object.isLong()) {
1120 | buffer.append(object.asLong());
1121 | } else if (object.isBoolean()) {
1122 | buffer.append(object.asBoolean());
1123 | } else if (object.isNull()) {
1124 | buffer.append("null");
1125 | } else
1126 | throw new JsonException("Unknown object type: " + object);
1127 | }
1128 |
1129 | /** More efficient than {@link #prettyPrint(PrettyPrintSettings)} but {@link PrettyPrintSettings#singleLineColumns} and
1130 | * {@link PrettyPrintSettings#wrapNumericArrays} are not supported. */
1131 | public void prettyPrint (OutputType outputType, Writer writer) throws IOException {
1132 | PrettyPrintSettings settings = new PrettyPrintSettings();
1133 | settings.outputType = outputType;
1134 | prettyPrint(this, writer, 0, settings);
1135 | }
1136 |
1137 | private void prettyPrint (JsonValue object, Writer writer, int indent, PrettyPrintSettings settings) throws IOException {
1138 | OutputType outputType = settings.outputType;
1139 | if (object.isObject()) {
1140 | if (object.child == null)
1141 | writer.append("{}");
1142 | else {
1143 | boolean newLines = !isFlat(object) || object.size > 6;
1144 | writer.append(newLines ? "{\n" : "{ ");
1145 | int i = 0;
1146 | for (JsonValue child = object.child; child != null; child = child.next) {
1147 | if (newLines) indent(indent, writer);
1148 | writer.append(outputType.quoteName(child.name));
1149 | writer.append(": ");
1150 | prettyPrint(child, writer, indent + 1, settings);
1151 | if ((!newLines || outputType != OutputType.minimal) && child.next != null) writer.append(',');
1152 | writer.append(newLines ? '\n' : ' ');
1153 | }
1154 | if (newLines) indent(indent - 1, writer);
1155 | writer.append('}');
1156 | }
1157 | } else if (object.isArray()) {
1158 | if (object.child == null)
1159 | writer.append("[]");
1160 | else {
1161 | boolean newLines = !isFlat(object);
1162 | writer.append(newLines ? "[\n" : "[ ");
1163 | int i = 0;
1164 | for (JsonValue child = object.child; child != null; child = child.next) {
1165 | if (newLines) indent(indent, writer);
1166 | prettyPrint(child, writer, indent + 1, settings);
1167 | if ((!newLines || outputType != OutputType.minimal) && child.next != null) writer.append(',');
1168 | writer.append(newLines ? '\n' : ' ');
1169 | }
1170 | if (newLines) indent(indent - 1, writer);
1171 | writer.append(']');
1172 | }
1173 | } else if (object.isString()) {
1174 | writer.append(outputType.quoteValue(object.asString()));
1175 | } else if (object.isDouble()) {
1176 | double doubleValue = object.asDouble();
1177 | long longValue = object.asLong();
1178 | writer.append(Double.toString(doubleValue == longValue ? longValue : doubleValue));
1179 | } else if (object.isLong()) {
1180 | writer.append(Long.toString(object.asLong()));
1181 | } else if (object.isBoolean()) {
1182 | writer.append(Boolean.toString(object.asBoolean()));
1183 | } else if (object.isNull()) {
1184 | writer.append("null");
1185 | } else
1186 | throw new JsonException("Unknown object type: " + object);
1187 | }
1188 |
1189 | static private boolean isFlat (JsonValue object) {
1190 | for (JsonValue child = object.child; child != null; child = child.next)
1191 | if (child.isObject() || child.isArray()) return false;
1192 | return true;
1193 | }
1194 |
1195 | static private boolean isNumeric (JsonValue object) {
1196 | for (JsonValue child = object.child; child != null; child = child.next)
1197 | if (!child.isNumber()) return false;
1198 | return true;
1199 | }
1200 |
1201 | static private void indent (int count, StringBuilder buffer) {
1202 | for (int i = 0; i < count; i++)
1203 | buffer.append('\t');
1204 | }
1205 |
1206 | static private void indent (int count, Writer buffer) throws IOException {
1207 | for (int i = 0; i < count; i++)
1208 | buffer.append('\t');
1209 | }
1210 |
1211 | public class JsonIterator implements Iterator, Iterable {
1212 | JsonValue entry = child;
1213 | JsonValue current;
1214 |
1215 | public boolean hasNext () {
1216 | return entry != null;
1217 | }
1218 |
1219 | public JsonValue next () {
1220 | current = entry;
1221 | if (current == null) throw new NoSuchElementException();
1222 | entry = current.next;
1223 | return current;
1224 | }
1225 |
1226 | public void remove () {
1227 | if (current.prev == null) {
1228 | child = current.next;
1229 | if (child != null) child.prev = null;
1230 | } else {
1231 | current.prev.next = current.next;
1232 | if (current.next != null) current.next.prev = current.prev;
1233 | }
1234 | size--;
1235 | }
1236 |
1237 | public Iterator iterator () {
1238 | return this;
1239 | }
1240 | }
1241 |
1242 | public enum ValueType {
1243 | object, array, stringValue, doubleValue, longValue, booleanValue, nullValue
1244 | }
1245 |
1246 | static public class PrettyPrintSettings {
1247 | public OutputType outputType;
1248 |
1249 | /** If an object on a single line fits this many columns, it won't wrap. */
1250 | public int singleLineColumns;
1251 |
1252 | /** Arrays of floats won't wrap. */
1253 | public boolean wrapNumericArrays;
1254 | }
1255 | }
1256 |
--------------------------------------------------------------------------------
/src/com/esotericsoftware/jsonbeans/JsonWriter.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011 See AUTHORS file.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | ******************************************************************************/
16 |
17 | package com.esotericsoftware.jsonbeans;
18 |
19 | import java.io.IOException;
20 | import java.io.Writer;
21 | import java.math.BigDecimal;
22 | import java.math.BigInteger;
23 | import java.util.ArrayList;
24 |
25 | /** Builder style API for emitting JSON.
26 | * @author Nathan Sweet */
27 | public class JsonWriter extends Writer {
28 | final Writer writer;
29 | private final ArrayList stack = new ArrayList();
30 | private JsonObject current;
31 | private boolean named;
32 | private OutputType outputType = OutputType.json;
33 | private boolean quoteLongValues = false;
34 |
35 | public JsonWriter (Writer writer) {
36 | this.writer = writer;
37 | }
38 |
39 | public Writer getWriter () {
40 | return writer;
41 | }
42 |
43 | /** Sets the type of JSON output. Default is {@link OutputType#minimal}. */
44 | public void setOutputType (OutputType outputType) {
45 | this.outputType = outputType;
46 | }
47 |
48 | /** When true, quotes long, double, BigInteger, BigDecimal types to prevent truncation in languages like JavaScript and PHP.
49 | * This is not necessary when using libgdx, which handles these types without truncation. Default is false. */
50 | public void setQuoteLongValues (boolean quoteLongValues) {
51 | this.quoteLongValues = quoteLongValues;
52 | }
53 |
54 | public JsonWriter name (String name) throws IOException {
55 | if (current == null || current.array) throw new IllegalStateException("Current item must be an object.");
56 | if (!current.needsComma)
57 | current.needsComma = true;
58 | else
59 | writer.write(',');
60 | writer.write(outputType.quoteName(name));
61 | writer.write(':');
62 | named = true;
63 | return this;
64 | }
65 |
66 | public JsonWriter object () throws IOException {
67 | requireCommaOrName();
68 | stack.add(current = new JsonObject(false));
69 | return this;
70 | }
71 |
72 | public JsonWriter array () throws IOException {
73 | requireCommaOrName();
74 | stack.add(current = new JsonObject(true));
75 | return this;
76 | }
77 |
78 | public JsonWriter value (Object value) throws IOException {
79 | if (quoteLongValues
80 | && (value instanceof Long || value instanceof Double || value instanceof BigDecimal || value instanceof BigInteger)) {
81 | value = value.toString();
82 | } else if (value instanceof Number) {
83 | Number number = (Number)value;
84 | long longValue = number.longValue();
85 | if (number.doubleValue() == longValue) value = longValue;
86 | }
87 | requireCommaOrName();
88 | writer.write(outputType.quoteValue(value));
89 | return this;
90 | }
91 |
92 | /** Writes the specified JSON value, without quoting or escaping. */
93 | public JsonWriter json (String json) throws IOException {
94 | requireCommaOrName();
95 | writer.write(json);
96 | return this;
97 | }
98 |
99 | private void requireCommaOrName () throws IOException {
100 | if (current == null) return;
101 | if (current.array) {
102 | if (!current.needsComma)
103 | current.needsComma = true;
104 | else
105 | writer.write(',');
106 | } else {
107 | if (!named) throw new IllegalStateException("Name must be set.");
108 | named = false;
109 | }
110 | }
111 |
112 | public JsonWriter object (String name) throws IOException {
113 | return name(name).object();
114 | }
115 |
116 | public JsonWriter array (String name) throws IOException {
117 | return name(name).array();
118 | }
119 |
120 | public JsonWriter set (String name, Object value) throws IOException {
121 | return name(name).value(value);
122 | }
123 |
124 | /** Writes the specified JSON value, without quoting or escaping. */
125 | public JsonWriter json (String name, String json) throws IOException {
126 | return name(name).json(json);
127 | }
128 |
129 | public JsonWriter pop () throws IOException {
130 | if (named) throw new IllegalStateException("Expected an object, array, or value since a name was set.");
131 | stack.remove(stack.size() - 1).close();
132 | current = stack.size() == 0 ? null : stack.get(stack.size() - 1);
133 | return this;
134 | }
135 |
136 | public void write (char[] cbuf, int off, int len) throws IOException {
137 | writer.write(cbuf, off, len);
138 | }
139 |
140 | public void flush () throws IOException {
141 | writer.flush();
142 | }
143 |
144 | public void close () throws IOException {
145 | while (stack.size() > 0)
146 | pop();
147 | writer.close();
148 | }
149 |
150 | private class JsonObject {
151 | final boolean array;
152 | boolean needsComma;
153 |
154 | JsonObject (boolean array) throws IOException {
155 | this.array = array;
156 | writer.write(array ? '[' : '{');
157 | }
158 |
159 | void close () throws IOException {
160 | writer.write(array ? ']' : '}');
161 | }
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/src/com/esotericsoftware/jsonbeans/Null.java:
--------------------------------------------------------------------------------
1 |
2 | package com.esotericsoftware.jsonbeans;
3 |
4 | import java.lang.annotation.Documented;
5 | import java.lang.annotation.ElementType;
6 | import java.lang.annotation.Target;
7 |
8 | /** An element with this annotation claims that the element may have a {@code null} value. Apart from documentation purposes this
9 | * annotation is intended to be used by static analysis tools to validate against probable runtime errors or contract violations.
10 | * @author maltaisn */
11 | @Documented
12 | @Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})
13 | public @interface Null {
14 | }
15 |
--------------------------------------------------------------------------------
/src/com/esotericsoftware/jsonbeans/ObjectMap.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright 2011 See AUTHORS file.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | ******************************************************************************/
16 |
17 | package com.esotericsoftware.jsonbeans;
18 |
19 | import java.util.ArrayList;
20 | import java.util.Arrays;
21 | import java.util.Iterator;
22 | import java.util.List;
23 | import java.util.NoSuchElementException;
24 |
25 | /** An unordered map where the keys and values are objects. Null keys are not allowed. No allocation is done except when growing
26 | * the table size.
27 | *
28 | * This class performs fast contains and remove (typically O(1), worst case O(n) but that is rare in practice). Add may be
29 | * slightly slower, depending on hash collisions. Hashcodes are rehashed to reduce collisions and the need to resize. Load factors
30 | * greater than 0.91 greatly increase the chances to resize to the next higher POT size.
31 | *
32 | * Unordered sets and maps are not designed to provide especially fast iteration.
33 | *
34 | * This implementation uses linear probing with the backward shift algorithm for removal. Hashcodes are rehashed using Fibonacci
35 | * hashing, instead of the more common power-of-two mask, to better distribute poor hashCodes (see Malte
37 | * Skarupke's blog post). Linear probing continues to work even when all hashCodes collide, just more slowly.
38 | * @author Nathan Sweet
39 | * @author Tommy Ettinger */
40 | public class ObjectMap implements Iterable> {
41 | static final Object dummy = new Object();
42 |
43 | public int size;
44 |
45 | K[] keyTable;
46 | V[] valueTable;
47 |
48 | float loadFactor;
49 | int threshold;
50 |
51 | /** Used by {@link #place(Object)} to bit shift the upper bits of a {@code long} into a usable range (>= 0 and <=
52 | * {@link #mask}). The shift can be negative, which is convenient to match the number of bits in mask: if mask is a 7-bit
53 | * number, a shift of -7 shifts the upper 7 bits into the lowest 7 positions. This class sets the shift > 32 and < 64,
54 | * which if used with an int will still move the upper bits of an int to the lower bits due to Java's implicit modulus on
55 | * shifts.
56 | *
57 | * {@link #mask} can also be used to mask the low bits of a number, which may be faster for some hashcodes, if
58 | * {@link #place(Object)} is overridden. */
59 | protected int shift;
60 |
61 | /** A bitmask used to confine hashcodes to the size of the table. Must be all 1 bits in its low positions, ie a power of two
62 | * minus 1. If {@link #place(Object)} is overriden, this can be used instead of {@link #shift} to isolate usable bits of a
63 | * hash. */
64 | protected int mask;
65 |
66 | /** Creates a new map with an initial capacity of 51 and a load factor of 0.8. */
67 | public ObjectMap () {
68 | this(51, 0.8f);
69 | }
70 |
71 | /** Creates a new map with a load factor of 0.8.
72 | * @param initialCapacity If not a power of two, it is increased to the next nearest power of two. */
73 | public ObjectMap (int initialCapacity) {
74 | this(initialCapacity, 0.8f);
75 | }
76 |
77 | /** Creates a new map with the specified initial capacity and load factor. This map will hold initialCapacity items before
78 | * growing the backing table.
79 | * @param initialCapacity If not a power of two, it is increased to the next nearest power of two. */
80 | public ObjectMap (int initialCapacity, float loadFactor) {
81 | if (loadFactor <= 0f || loadFactor >= 1f)
82 | throw new IllegalArgumentException("loadFactor must be > 0 and < 1: " + loadFactor);
83 | this.loadFactor = loadFactor;
84 |
85 | int tableSize = tableSize(initialCapacity, loadFactor);
86 | threshold = (int)(tableSize * loadFactor);
87 | mask = tableSize - 1;
88 | shift = Long.numberOfLeadingZeros(mask);
89 |
90 | keyTable = (K[])new Object[tableSize];
91 | valueTable = (V[])new Object[tableSize];
92 | }
93 |
94 | /** Creates a new map identical to the specified map. */
95 | public ObjectMap (ObjectMap extends K, ? extends V> map) {
96 | this((int)(map.keyTable.length * map.loadFactor), map.loadFactor);
97 | System.arraycopy(map.keyTable, 0, keyTable, 0, map.keyTable.length);
98 | System.arraycopy(map.valueTable, 0, valueTable, 0, map.valueTable.length);
99 | size = map.size;
100 | }
101 |
102 | /** Returns an index >= 0 and <= {@link #mask} for the specified {@code item}.
103 | *
104 | * The default implementation uses Fibonacci hashing on the item's {@link Object#hashCode()}: the hashcode is multiplied by a
105 | * long constant (2 to the 64th, divided by the golden ratio) then the uppermost bits are shifted into the lowest positions to
106 | * obtain an index in the desired range. Multiplication by a long may be slower than int (eg on GWT) but greatly improves
107 | * rehashing, allowing even very poor hashcodes, such as those that only differ in their upper bits, to be used without high
108 | * collision rates. Fibonacci hashing has increased collision rates when all or most hashcodes are multiples of larger
109 | * Fibonacci numbers (see Malte
111 | * Skarupke's blog post).
112 | *
113 | * This method can be overriden to customizing hashing. This may be useful eg in the unlikely event that most hashcodes are
114 | * Fibonacci numbers, if keys provide poor or incorrect hashcodes, or to simplify hashing if keys provide high quality
115 | * hashcodes and don't need Fibonacci hashing: {@code return item.hashCode() & mask;} */
116 | protected int place (K item) {
117 | return (int)(item.hashCode() * 0x9E3779B97F4A7C15L >>> shift);
118 | }
119 |
120 | /** Returns the index of the key if already present, else -(index + 1) for the next empty index. This can be overridden in this
121 | * pacakge to compare for equality differently than {@link Object#equals(Object)}. */
122 | int locateKey (K key) {
123 | if (key == null) throw new IllegalArgumentException("key cannot be null.");
124 | K[] keyTable = this.keyTable;
125 | for (int i = place(key);; i = i + 1 & mask) {
126 | K other = keyTable[i];
127 | if (other == null) return -(i + 1); // Empty space is available.
128 | if (other.equals(key)) return i; // Same key was found.
129 | }
130 | }
131 |
132 | /** Returns the old value associated with the specified key, or null. */
133 | @Null
134 | public V put (K key, @Null V value) {
135 | int i = locateKey(key);
136 | if (i >= 0) { // Existing key was found.
137 | V oldValue = valueTable[i];
138 | valueTable[i] = value;
139 | return oldValue;
140 | }
141 | i = -(i + 1); // Empty space was found.
142 | keyTable[i] = key;
143 | valueTable[i] = value;
144 | if (++size >= threshold) resize(keyTable.length << 1);
145 | return null;
146 | }
147 |
148 | public void putAll (ObjectMap extends K, ? extends V> map) {
149 | ensureCapacity(map.size);
150 | K[] keyTable = map.keyTable;
151 | V[] valueTable = map.valueTable;
152 | K key;
153 | for (int i = 0, n = keyTable.length; i < n; i++) {
154 | key = keyTable[i];
155 | if (key != null) put(key, valueTable[i]);
156 | }
157 | }
158 |
159 | /** Skips checks for existing keys, doesn't increment size. */
160 | private void putResize (K key, @Null V value) {
161 | K[] keyTable = this.keyTable;
162 | for (int i = place(key);; i = (i + 1) & mask) {
163 | if (keyTable[i] == null) {
164 | keyTable[i] = key;
165 | valueTable[i] = value;
166 | return;
167 | }
168 | }
169 | }
170 |
171 | /** Returns the value for the specified key, or null if the key is not in the map. */
172 | @Null
173 | public V get (T key) {
174 | int i = locateKey(key);
175 | return i < 0 ? null : valueTable[i];
176 | }
177 |
178 | /** Returns the value for the specified key, or the default value if the key is not in the map. */
179 | public V get (K key, @Null V defaultValue) {
180 | int i = locateKey(key);
181 | return i < 0 ? defaultValue : valueTable[i];
182 | }
183 |
184 | @Null
185 | public V remove (K key) {
186 | int i = locateKey(key);
187 | if (i < 0) return null;
188 | K[] keyTable = this.keyTable;
189 | V[] valueTable = this.valueTable;
190 | V oldValue = valueTable[i];
191 | int mask = this.mask, next = i + 1 & mask;
192 | while ((key = keyTable[next]) != null) {
193 | int placement = place(key);
194 | if ((next - placement & mask) > (i - placement & mask)) {
195 | keyTable[i] = key;
196 | valueTable[i] = valueTable[next];
197 | i = next;
198 | }
199 | next = next + 1 & mask;
200 | }
201 | keyTable[i] = null;
202 | valueTable[i] = null;
203 | size--;
204 | return oldValue;
205 | }
206 |
207 | /** Returns true if the map has one or more items. */
208 | public boolean notEmpty () {
209 | return size > 0;
210 | }
211 |
212 | /** Returns true if the map is empty. */
213 | public boolean isEmpty () {
214 | return size == 0;
215 | }
216 |
217 | /** Reduces the size of the backing arrays to be the specified capacity / loadFactor, or less. If the capacity is already less,
218 | * nothing is done. If the map contains more items than the specified capacity, the next highest power of two capacity is used
219 | * instead. */
220 | public void shrink (int maximumCapacity) {
221 | if (maximumCapacity < 0) throw new IllegalArgumentException("maximumCapacity must be >= 0: " + maximumCapacity);
222 | int tableSize = tableSize(maximumCapacity, loadFactor);
223 | if (keyTable.length > tableSize) resize(tableSize);
224 | }
225 |
226 | /** Clears the map and reduces the size of the backing arrays to be the specified capacity / loadFactor, if they are larger. */
227 | public void clear (int maximumCapacity) {
228 | int tableSize = tableSize(maximumCapacity, loadFactor);
229 | if (keyTable.length <= tableSize) {
230 | clear();
231 | return;
232 | }
233 | size = 0;
234 | resize(tableSize);
235 | }
236 |
237 | public void clear () {
238 | if (size == 0) return;
239 | size = 0;
240 | Arrays.fill(keyTable, null);
241 | Arrays.fill(valueTable, null);
242 | }
243 |
244 | /** Returns true if the specified value is in the map. Note this traverses the entire map and compares every value, which may
245 | * be an expensive operation.
246 | * @param identity If true, uses == to compare the specified value with values in the map. If false, uses
247 | * {@link #equals(Object)}. */
248 | public boolean containsValue (@Null Object value, boolean identity) {
249 | V[] valueTable = this.valueTable;
250 | if (value == null) {
251 | K[] keyTable = this.keyTable;
252 | for (int i = valueTable.length - 1; i >= 0; i--)
253 | if (keyTable[i] != null && valueTable[i] == null) return true;
254 | } else if (identity) {
255 | for (int i = valueTable.length - 1; i >= 0; i--)
256 | if (valueTable[i] == value) return true;
257 | } else {
258 | for (int i = valueTable.length - 1; i >= 0; i--)
259 | if (value.equals(valueTable[i])) return true;
260 | }
261 | return false;
262 | }
263 |
264 | public boolean containsKey (K key) {
265 | return locateKey(key) >= 0;
266 | }
267 |
268 | /** Returns the key for the specified value, or null if it is not in the map. Note this traverses the entire map and compares
269 | * every value, which may be an expensive operation.
270 | * @param identity If true, uses == to compare the specified value with values in the map. If false, uses
271 | * {@link #equals(Object)}. */
272 | @Null
273 | public K findKey (@Null Object value, boolean identity) {
274 | V[] valueTable = this.valueTable;
275 | if (value == null) {
276 | K[] keyTable = this.keyTable;
277 | for (int i = valueTable.length - 1; i >= 0; i--)
278 | if (keyTable[i] != null && valueTable[i] == null) return keyTable[i];
279 | } else if (identity) {
280 | for (int i = valueTable.length - 1; i >= 0; i--)
281 | if (valueTable[i] == value) return keyTable[i];
282 | } else {
283 | for (int i = valueTable.length - 1; i >= 0; i--)
284 | if (value.equals(valueTable[i])) return keyTable[i];
285 | }
286 | return null;
287 | }
288 |
289 | /** Increases the size of the backing array to accommodate the specified number of additional items / loadFactor. Useful before
290 | * adding many items to avoid multiple backing array resizes. */
291 | public void ensureCapacity (int additionalCapacity) {
292 | int tableSize = tableSize(size + additionalCapacity, loadFactor);
293 | if (keyTable.length < tableSize) resize(tableSize);
294 | }
295 |
296 | final void resize (int newSize) {
297 | int oldCapacity = keyTable.length;
298 | threshold = (int)(newSize * loadFactor);
299 | mask = newSize - 1;
300 | shift = Long.numberOfLeadingZeros(mask);
301 |
302 | K[] oldKeyTable = keyTable;
303 | V[] oldValueTable = valueTable;
304 |
305 | keyTable = (K[])new Object[newSize];
306 | valueTable = (V[])new Object[newSize];
307 |
308 | if (size > 0) {
309 | for (int i = 0; i < oldCapacity; i++) {
310 | K key = oldKeyTable[i];
311 | if (key != null) putResize(key, oldValueTable[i]);
312 | }
313 | }
314 | }
315 |
316 | public int hashCode () {
317 | int h = size;
318 | K[] keyTable = this.keyTable;
319 | V[] valueTable = this.valueTable;
320 | for (int i = 0, n = keyTable.length; i < n; i++) {
321 | K key = keyTable[i];
322 | if (key != null) {
323 | h += key.hashCode();
324 | V value = valueTable[i];
325 | if (value != null) h += value.hashCode();
326 | }
327 | }
328 | return h;
329 | }
330 |
331 | public boolean equals (Object obj) {
332 | if (obj == this) return true;
333 | if (!(obj instanceof ObjectMap)) return false;
334 | ObjectMap other = (ObjectMap)obj;
335 | if (other.size != size) return false;
336 | K[] keyTable = this.keyTable;
337 | V[] valueTable = this.valueTable;
338 | for (int i = 0, n = keyTable.length; i < n; i++) {
339 | K key = keyTable[i];
340 | if (key != null) {
341 | V value = valueTable[i];
342 | if (value == null) {
343 | if (other.get(key, dummy) != null) return false;
344 | } else {
345 | if (!value.equals(other.get(key))) return false;
346 | }
347 | }
348 | }
349 | return true;
350 | }
351 |
352 | /** Uses == for comparison of each value. */
353 | public boolean equalsIdentity (@Null Object obj) {
354 | if (obj == this) return true;
355 | if (!(obj instanceof ObjectMap)) return false;
356 | ObjectMap other = (ObjectMap)obj;
357 | if (other.size != size) return false;
358 | K[] keyTable = this.keyTable;
359 | V[] valueTable = this.valueTable;
360 | for (int i = 0, n = keyTable.length; i < n; i++) {
361 | K key = keyTable[i];
362 | if (key != null && valueTable[i] != other.get(key, dummy)) return false;
363 | }
364 | return true;
365 | }
366 |
367 | public String toString (String separator) {
368 | return toString(separator, false);
369 | }
370 |
371 | public String toString () {
372 | return toString(", ", true);
373 | }
374 |
375 | private String toString (String separator, boolean braces) {
376 | if (size == 0) return braces ? "{}" : "";
377 | java.lang.StringBuilder buffer = new java.lang.StringBuilder(32);
378 | if (braces) buffer.append('{');
379 | K[] keyTable = this.keyTable;
380 | V[] valueTable = this.valueTable;
381 | int i = keyTable.length;
382 | while (i-- > 0) {
383 | K key = keyTable[i];
384 | if (key == null) continue;
385 | buffer.append(key == this ? "(this)" : key);
386 | buffer.append('=');
387 | V value = valueTable[i];
388 | buffer.append(value == this ? "(this)" : value);
389 | break;
390 | }
391 | while (i-- > 0) {
392 | K key = keyTable[i];
393 | if (key == null) continue;
394 | buffer.append(separator);
395 | buffer.append(key == this ? "(this)" : key);
396 | buffer.append('=');
397 | V value = valueTable[i];
398 | buffer.append(value == this ? "(this)" : value);
399 | }
400 | if (braces) buffer.append('}');
401 | return buffer.toString();
402 | }
403 |
404 | public Entries iterator () {
405 | return entries();
406 | }
407 |
408 | /** Returns an iterator for the entries in the map. Remove is supported. */
409 | public Entries entries () {
410 | return new Entries(this);
411 | }
412 |
413 | /** Returns an iterator for the values in the map. Remove is supported. */
414 | public Values values () {
415 | return new Values(this);
416 | }
417 |
418 | /** Returns an iterator for the keys in the map. Remove is supported. */
419 | public Keys keys () {
420 | return new Keys(this);
421 | }
422 |
423 | static public int tableSize (int capacity, float loadFactor) {
424 | if (capacity < 0) throw new IllegalArgumentException("capacity must be >= 0: " + capacity);
425 | int tableSize = nextPowerOfTwo(Math.max(2, (int)Math.ceil(capacity / loadFactor)));
426 | if (tableSize > 1 << 30) throw new IllegalArgumentException("The required capacity is too large: " + capacity);
427 | return tableSize;
428 | }
429 |
430 | static public int nextPowerOfTwo (int value) {
431 | if (value == 0) return 1;
432 | value--;
433 | value |= value >> 1;
434 | value |= value >> 2;
435 | value |= value >> 4;
436 | value |= value >> 8;
437 | value |= value >> 16;
438 | return value + 1;
439 | }
440 |
441 | static public class Entry {
442 | public K key;
443 | @Null public V value;
444 |
445 | public String toString () {
446 | return key + "=" + value;
447 | }
448 | }
449 |
450 | static private abstract class MapIterator implements Iterable, Iterator {
451 | public boolean hasNext;
452 |
453 | final ObjectMap map;
454 | int nextIndex, currentIndex;
455 |
456 | public MapIterator (ObjectMap map) {
457 | this.map = map;
458 | reset();
459 | }
460 |
461 | public void reset () {
462 | currentIndex = -1;
463 | nextIndex = -1;
464 | findNextIndex();
465 | }
466 |
467 | void findNextIndex () {
468 | K[] keyTable = map.keyTable;
469 | for (int n = keyTable.length; ++nextIndex < n;) {
470 | if (keyTable[nextIndex] != null) {
471 | hasNext = true;
472 | return;
473 | }
474 | }
475 | hasNext = false;
476 | }
477 |
478 | public void remove () {
479 | int i = currentIndex;
480 | if (i < 0) throw new IllegalStateException("next must be called before remove.");
481 | K[] keyTable = map.keyTable;
482 | V[] valueTable = map.valueTable;
483 | int mask = map.mask, next = i + 1 & mask;
484 | K key;
485 | while ((key = keyTable[next]) != null) {
486 | int placement = map.place(key);
487 | if ((next - placement & mask) > (i - placement & mask)) {
488 | keyTable[i] = key;
489 | valueTable[i] = valueTable[next];
490 | i = next;
491 | }
492 | next = next + 1 & mask;
493 | }
494 | keyTable[i] = null;
495 | valueTable[i] = null;
496 | map.size--;
497 | if (i != currentIndex) --nextIndex;
498 | currentIndex = -1;
499 | }
500 | }
501 |
502 | static public class Entries extends MapIterator> {
503 | Entry entry = new Entry();
504 |
505 | public Entries (ObjectMap map) {
506 | super(map);
507 | }
508 |
509 | /** Note the same entry instance is returned each time this method is called. */
510 | public Entry next () {
511 | if (!hasNext) throw new NoSuchElementException();
512 | K[] keyTable = map.keyTable;
513 | entry.key = keyTable[nextIndex];
514 | entry.value = map.valueTable[nextIndex];
515 | currentIndex = nextIndex;
516 | findNextIndex();
517 | return entry;
518 | }
519 |
520 | public boolean hasNext () {
521 | return hasNext;
522 | }
523 |
524 | public Entries iterator () {
525 | return this;
526 | }
527 | }
528 |
529 | static public class Values extends MapIterator