Skip to content
This repository was archived by the owner on May 30, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
150 commits
Select commit Hold shift + click to select a range
db568d4
initial implementation of Java common SDK code (#1)
eli-darkly Mar 20, 2020
374e08e
stick with Java 7 for Android compatibility (#2)
eli-darkly Apr 15, 2020
f1b9cb4
add getters to EvaluationReason and hide its subclasses
eli-darkly Apr 20, 2020
e86ced3
completely remove EvaluationReason subclasses
eli-darkly Apr 20, 2020
a8d1a91
Merge pull request #3 from launchdarkly/eb/ch74282/hide-reason-subcla…
eli-darkly Apr 20, 2020
0ff060b
Merge pull request #4 from launchdarkly/eb/ch74282/remove-reason-subc…
eli-darkly Apr 20, 2020
d440fd0
add JSON helpers, better serialization logic, and Gson adapter
eli-darkly Apr 20, 2020
04eaf1a
javadoc fixes
eli-darkly Apr 21, 2020
433a1e7
remove @since tags, misc doc fixes, add note about changelogging
eli-darkly Apr 21, 2020
55e3c1b
Merge branch 'eb/ch74370/javadoc-since' into eb/ch73994/gson-interop
eli-darkly Apr 21, 2020
fe6f0eb
fix sample code
eli-darkly Apr 22, 2020
97a8fa3
rename Gson adapter, use factory method
eli-darkly Apr 22, 2020
2c5cc2b
Merge pull request #5 from launchdarkly/eb/ch74370/javadoc-since
eli-darkly Apr 22, 2020
511c9e2
improve and rigorously test equals() for all immutable types
eli-darkly Apr 24, 2020
525daa6
simplifying
eli-darkly Apr 24, 2020
3e222c3
simplifying
eli-darkly Apr 24, 2020
d003179
make EvaluationDetail non-nullable + use boolean singletons
eli-darkly Apr 24, 2020
b810cd7
hide Gson in pom
eli-darkly Apr 24, 2020
129d5b6
doc additions
eli-darkly Apr 24, 2020
0ab6d7c
Merge pull request #8 from launchdarkly/eb/ch74915/non-null-index
eli-darkly Apr 27, 2020
36d3fba
Merge pull request #6 from launchdarkly/eb/ch73994/gson-interop
eli-darkly Apr 27, 2020
939b879
Merge branch 'master' into eb/ch68094/test-equals
eli-darkly Apr 27, 2020
6136698
javadoc fixes
eli-darkly Apr 27, 2020
fa2dd6c
Merge branch 'master' into eb/ch68094/test-equals
eli-darkly Apr 27, 2020
9bd6dbb
Merge pull request #7 from launchdarkly/eb/ch68094/test-equals
eli-darkly Apr 28, 2020
5458738
add Jackson adapter (#9)
eli-darkly Apr 28, 2020
5635a1d
more predictable exception behavior for LDValue.parse()
eli-darkly Apr 28, 2020
4ac7396
Merge pull request #11 from launchdarkly/eb/ch73994/parse-exception
eli-darkly Apr 28, 2020
21e9167
fix the JSON behavior of EvaluationDetail (#10)
eli-darkly Apr 28, 2020
7c29e7b
maximize and enforce test coverage (#12)
eli-darkly Apr 28, 2020
7a2aa77
build and test in Android! (#13)
eli-darkly Apr 29, 2020
aa9f794
Merge branch 'master' of github.com:launchdarkly/java-sdk-common
eli-darkly Apr 29, 2020
d5c3b0d
ensure real nulls can't be stored in an LDValue
eli-darkly May 23, 2020
9e200fb
clean up some unnecessary coverage warnings
eli-darkly May 23, 2020
08f2b21
Merge pull request #15 from launchdarkly/eb/ch77641/null-safety
eli-darkly May 27, 2020
8fbc7a0
more convenient location for coverage reports
eli-darkly May 27, 2020
fbe209b
remove rc1 from changelog
eli-darkly Jun 1, 2020
7f83233
Merge branch 'master' of github.com:launchdarkly/java-sdk-common
eli-darkly Jun 2, 2020
ac4a2cd
Removed the guides link
bwoskow-ld Feb 3, 2021
1cad8c5
Use Android machine image for CircleCI Android tests. (#17)
gwhelanLD Mar 30, 2021
17f0b0b
improve Gson integration using reader/writer delegation + add LDValue…
eli-darkly Mar 30, 2021
96931b3
add LDValue.arrayOf() + misc javadoc fixes
eli-darkly Mar 30, 2021
99c51e9
Merge pull request #19 from launchdarkly/eb/ch104357/array-of
eli-darkly Mar 30, 2021
41058b9
Add inExperiment attribute to FALLTHROUGH and RULE_MATCH reasons
robertjneal Apr 3, 2021
0ea70c9
fix javadoc
robertjneal Apr 3, 2021
532fa70
Update src/main/java/com/launchdarkly/sdk/EvaluationReason.java
robertjneal Apr 6, 2021
7526a58
Update src/main/java/com/launchdarkly/sdk/EvaluationReason.java
robertjneal Apr 6, 2021
e3c06ec
Update src/main/java/com/launchdarkly/sdk/EvaluationReason.java
robertjneal Apr 6, 2021
0b6aef3
Update src/main/java/com/launchdarkly/sdk/EvaluationReason.java
robertjneal Apr 6, 2021
6e1b358
respond to review comments
robertjneal Apr 6, 2021
76533ce
fix test
robertjneal Apr 6, 2021
a9c82c7
Merge pull request #20 from launchdarkly/rneal/ch101658/add-inExperim…
robertjneal Apr 6, 2021
b468c4e
javadoc fix
eli-darkly Apr 8, 2021
fede11e
Merge branch 'master' into exp-alloc
eli-darkly Apr 8, 2021
a5abd08
add validation of javadoc build in CI
eli-darkly Apr 8, 2021
1e11684
add another Eclipse metadata exclusion to .gitignore
eli-darkly Apr 8, 2021
bceedef
Merge pull request #21 from launchdarkly/eb/ch105283/check-javadoc
eli-darkly Apr 8, 2021
3a8aa98
partially revert ch103941 fix that doesn't work in the Java SDK
eli-darkly Apr 14, 2021
3f02d6d
Merge pull request #22 from launchdarkly/eb/ch103941/revert-gson-adapter
eli-darkly Apr 14, 2021
7dee1cf
Merge branch 'master' into exp-alloc
eli-darkly Apr 14, 2021
36fe80f
unrevert some JSON improvements from previous revert
eli-darkly Apr 16, 2021
cfdece2
Merge pull request #23 from launchdarkly/eb/ch103941/json-partial-unr…
eli-darkly Apr 17, 2021
9d50c7e
improve Gson integration using reader/writer delegation - take 2
eli-darkly Apr 19, 2021
df89903
Merge pull request #24 from launchdarkly/eb/ch103941/gson-adapter-fix
eli-darkly Apr 21, 2021
b8ace9b
better Jackson adapter + misc JSON test improvements (#18)
eli-darkly Apr 21, 2021
cd9525e
omit redundant method call (this commit was mistakenly left out of th…
eli-darkly Apr 21, 2021
62c6988
merge from public after release
LaunchDarklyCI Apr 22, 2021
f5680a1
Merge branch 'master' of github.com:launchdarkly/java-sdk-common
eli-darkly Apr 22, 2021
08c734f
avoid unnecessarily adding ".0" to JSON numbers (#25)
eli-darkly Apr 22, 2021
3dbe3f2
merge from public after release
LaunchDarklyCI Apr 22, 2021
6a5a5f6
update Gradle to 6.8.3
eli-darkly May 5, 2021
113c27d
Merge pull request #26 from launchdarkly/eb/ch104534/gradle-update
eli-darkly May 5, 2021
b91dd33
Kotlinize main build script
eli-darkly May 5, 2021
9088dff
rm obsolete comments
eli-darkly May 5, 2021
41a846b
Merge pull request #27 from launchdarkly/eb/ch107687/gradle-kotlin
eli-darkly May 6, 2021
b5c338e
bump Jackson compile-time dependency to 2.10.5.1 due to CVE-2020-2564…
eli-darkly Jun 14, 2021
b9116a8
exclude Gson & Jackson from published dependencies in a more correct …
eli-darkly Jun 14, 2021
242a8f5
merge from public after release
LaunchDarklyCI Jun 15, 2021
6589606
Merge branch 'exp-alloc'
eli-darkly Jun 17, 2021
c69e187
merge from public after release
LaunchDarklyCI Jun 17, 2021
2a59948
refactor build scripts using buildSrc (#29)
eli-darkly Jun 29, 2021
03adb77
use Releaser v2 config format + newer CircleCI images (#33)
eli-darkly Sep 14, 2021
979d0de
Updates docs URLs
ember-stevens Sep 26, 2021
dee5dab
Merge pull request #34 from launchdarkly/emberstevens/sc-118589/url-a…
ember-stevens Sep 27, 2021
056ff10
update Gson to 2.8.9
eli-darkly Nov 30, 2021
e494dbc
Merge pull request #35 from launchdarkly/eb/sc-132830/gson-version
eli-darkly Nov 30, 2021
8c105c6
merge from public after release
Nov 30, 2021
30213bc
use Gradle 7
eli-darkly Jan 8, 2022
5ef951c
don't use jcenter
eli-darkly Jan 8, 2022
913f2ef
add Java 17 to CI
eli-darkly Jan 8, 2022
2a6287a
Merge pull request #37 from launchdarkly/eb/sc-122485/gradle7
eli-darkly Jan 8, 2022
8f301f0
Merge pull request #38 from launchdarkly/eb/sc-122485/java17
eli-darkly Jan 8, 2022
e8a5373
also test Java 17 in Windows
eli-darkly Jan 8, 2022
ceb6a47
Merge pull request #39 from launchdarkly/eb/sc-122485/java17win
eli-darkly Jan 10, 2022
c589a19
don't suppress null property values when we serialize with Gson
eli-darkly Jan 24, 2022
292f33b
Merge pull request #40 from launchdarkly/eb/sc-139316/serialize-nulls
eli-darkly Jan 25, 2022
e0e389c
merge from public after release
Jan 25, 2022
d3b2369
remove obsolete publish-docs script
eli-darkly Jan 25, 2022
4d93f82
Merge pull request #41 from launchdarkly/eb/sc-139456/no-git-publish
eli-darkly Jan 25, 2022
9a05d5a
Merge feature branch big-segments for 1.3.0 release (#42)
gwhelanLD Jan 28, 2022
27ec95b
merge from public after release
Jan 29, 2022
90b5c5e
Fix link to CONTRIBUTING.md
aengelberg May 12, 2022
53bfa34
Merge pull request #43 from launchdarkly/fix-readme-link
aengelberg May 12, 2022
d6a5594
attribute reference type
eli-darkly Jul 11, 2022
4b68341
misc cleanup, test coverage
eli-darkly Jul 11, 2022
eeb0921
add context type & builders
eli-darkly Jul 12, 2022
b68142d
LDContext JSON marshaling & unmarshaling
eli-darkly Jul 12, 2022
ea4dfea
javadoc fix
eli-darkly Jul 12, 2022
046feb2
javadoc fix
eli-darkly Jul 12, 2022
c172113
store multiContexts as an array rather than a List
eli-darkly Jul 12, 2022
d9b41b4
Merge branch 'eb/sc-156589/contexts-2-ldcontext' into eb/sc-156589/co…
eli-darkly Jul 12, 2022
8ee8f75
revert accidental change
eli-darkly Jul 12, 2022
0496d90
add type adapter for ContextKind
eli-darkly Jul 13, 2022
66a4f6f
add null check/defaulting on getIndividualContext by kind
eli-darkly Jul 13, 2022
ccb24de
Merge branch 'eb/sc-156589/contexts-2-ldcontext' into eb/sc-156589/co…
eli-darkly Jul 13, 2022
5131647
fix context kind/key validation logic
eli-darkly Jul 14, 2022
c68bb39
don't allow deserializing an empty string
eli-darkly Jul 15, 2022
a57f4bb
remove use of isBlank which requires Java 11
eli-darkly Jul 15, 2022
5772ad0
ContextKind should be Comparable since it is string-like
eli-darkly Jul 18, 2022
791a7ae
Merge branch 'eb/sc-156589/contexts-2-ldcontext' into eb/sc-156589/co…
eli-darkly Jul 18, 2022
cf9b434
Merge branch 'eb/sc-156589/contexts-3-json' into eb/sc-156589/context…
eli-darkly Jul 18, 2022
0d5d498
Merge pull request #45 from launchdarkly/eb/sc-156589/contexts-1-attr…
eli-darkly Jul 19, 2022
ae042b3
Merge pull request #46 from launchdarkly/eb/sc-156589/contexts-2-ldco…
eli-darkly Jul 21, 2022
307d464
Merge pull request #47 from launchdarkly/eb/sc-156589/contexts-3-json
eli-darkly Jul 21, 2022
3fa3f76
Merge pull request #48 from launchdarkly/eb/sc-156589/contexts-4-vali…
eli-darkly Jul 21, 2022
627e2d5
use percent-encoding for specific characters in fully-qualified key, …
eli-darkly Jul 27, 2022
40065f3
flatten nested multi-kind contexts when building a multi-kind context
eli-darkly Jul 27, 2022
aebffa9
Merge pull request #49 from launchdarkly/eb/sc-161831/percent-encoding
eli-darkly Jul 29, 2022
a1adba4
Merge pull request #50 from launchdarkly/eb/sc-156589/nested-multi
eli-darkly Jul 29, 2022
87bb3be
fix user JSON validation to require that "custom" is an object or null
eli-darkly Jul 29, 2022
384c3aa
Merge pull request #51 from launchdarkly/eb/sc-156589/user-json-valid…
eli-darkly Aug 2, 2022
7ac24b9
attribute ref components are always properties, not array indices
eli-darkly Sep 29, 2022
3e80fd3
disable Windows Java 11 build
eli-darkly Oct 3, 2022
98d2519
Merge pull request #53 from launchdarkly/eb/sc-171428/no-windows-java-11
eli-darkly Oct 4, 2022
f5248ce
Merge branch 'main' into u2c
eli-darkly Oct 4, 2022
7da162f
Merge branch 'u2c' into eb/sc-171012/no-attr-ref-array-index
eli-darkly Oct 4, 2022
37093f9
Merge pull request #52 from launchdarkly/eb/sc-171012/no-attr-ref-arr…
eli-darkly Oct 7, 2022
f1065bb
remove secondary meta-attribute
eli-darkly Oct 14, 2022
4277c6e
remove LDUser as a concrete type
eli-darkly Oct 14, 2022
2abd2a0
test coverage
eli-darkly Oct 14, 2022
dec076a
update doc comment
eli-darkly Oct 21, 2022
c37d3c4
Merge pull request #54 from launchdarkly/eb/sc-156589/no-secondary
eli-darkly Oct 21, 2022
edc4872
Merge pull request #55 from launchdarkly/eb/sc-156589/no-user
eli-darkly Oct 21, 2022
83a9841
re-add LDUser type, add conversion to LDContext (#56)
eli-darkly Nov 18, 2022
6893e64
allow anonymous user with null key to be converted to a context
eli-darkly Nov 19, 2022
4feece6
doc comment improvements for user/context types
eli-darkly Dec 1, 2022
271800a
Merge branch 'main' of github.com:launchdarkly/java-sdk-common
eli-darkly Dec 1, 2022
c06ebe3
Merge branch 'u2c'
eli-darkly Dec 1, 2022
6f4c7b9
Merge pull request #57 from launchdarkly/eb/sc-177157/user-to-context…
eli-darkly Dec 1, 2022
9849861
Merge pull request #58 from launchdarkly/eb/sc-156596/u2c-comments
eli-darkly Dec 1, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ workflows:
with-coverage: true
requires:
- build-linux
- build-test-windows:
name: Java 11 - Windows - OpenJDK
openjdk-version: 11.0.2.01
# Windows Java 11 build is temporarily disabled - see story 171428
# - test-windows:
# name: Java 11 - Windows - OpenJDK
# openjdk-version: 11.0.2.01
- build-test-windows:
name: Java 17 - Windows - OpenJDK
openjdk-version: 17.0.1
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ This version of the library works with Java 7 and above.

## Contributing

See [Contributing](https://github.com/launchdarkly/dotnet-sdk-common/blob/master/CONTRIBUTING.md).
See [Contributing](CONTRIBUTING.md).

## About LaunchDarkly

Expand Down
1 change: 1 addition & 0 deletions buildSrc/src/main/kotlin/TestCoverageOverrides.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ object TestCoverageOverrides {
val methodsWithMissedLineCount = mapOf(
"EvaluationReason.error(com.launchdarkly.sdk.EvaluationReason.ErrorKind)" to 1,
"EvaluationReasonTypeAdapter.parse(com.google.gson.stream.JsonReader)" to 1,
"LDContext.urlEncodeKey(java.lang.String)" to 2,
"LDValue.equals(java.lang.Object)" to 1,
"LDValueTypeAdapter.read(com.google.gson.stream.JsonReader)" to 1,
"json.LDGson.LDTypeAdapter.write(com.google.gson.stream.JsonWriter, java.lang.Object)" to 1,
Expand Down
291 changes: 291 additions & 0 deletions src/main/java/com/launchdarkly/sdk/AttributeRef.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
package com.launchdarkly.sdk;

import java.util.HashMap;
import java.util.Map;

import com.google.gson.annotations.JsonAdapter;
import com.launchdarkly.sdk.json.JsonSerializable;

/**
* An attribute name or path expression identifying a value within an {@link LDContext}.
* <p>
* Applications are unlikely to need to use the AttributeRef type directly, but see below
* for details of the string attribute reference syntax used by methods like
* {@link ContextBuilder#privateAttributes(String...)}.
* <p>
* The reason to use this type directly is to avoid repetitive string parsing in code where
* efficiency is a priority; AttributeRef parses its contents once when it is created, and
* is immutable afterward. If an AttributeRef instance was created from an invalid string,
* it is considered invalid and its {@link #getError()} method will return a non-null error.
* <p>
* The string representation of an attribute reference in LaunchDarkly JSON data uses the
* following syntax:
* <ul>
* <li> If the first character is not a slash, the string is interpreted literally as an
* attribute name. An attribute name can contain any characters, but must not be empty. </li>
* <li> If the first character is a slash, the string is interpreted as a slash-delimited
* path where the first path component is an attribute name, and each subsequent path
* component is the name of a property in a JSON object. Any instances of the characters "/"
* or "~" in a path component are escaped as "~1" or "~0" respectively. This syntax
* deliberately resembles JSON Pointer, but no JSON Pointer behaviors other than those
* mentioned here are supported. </li>
* </ul>
*/
@JsonAdapter(AttributeRefTypeAdapter.class)
public final class AttributeRef implements JsonSerializable, Comparable<AttributeRef> {
private static final Map<String, AttributeRef> COMMON_LITERALS = makeLiteralsMap(
"kind", "key", "name", "anonymous", // built-ins
"email", "firstName", "lastName", "country", "ip", "avatar" // frequently used custom attributes
);

private final String error;
private final String rawPath;
private final String singlePathComponent;
private final String[] components;

private AttributeRef(String rawPath, String singlePathComponent, String[] components) {
this.error = null;
this.rawPath = rawPath == null ? "" : rawPath;
this.singlePathComponent = singlePathComponent;
this.components = components;
}

private AttributeRef(String error, String rawPath) {
this.error = error;
this.rawPath = rawPath == null ? "" : rawPath;
this.singlePathComponent = null;
this.components = null;
}

/**
* Creates an AttributeRef from a string. For the supported syntax and examples, see
* comments on the {@link AttributeRef} type.
* <p>
* This method always returns an AttributeRef that preserves the original string, even if
* validation fails, so that calling {@link #toString()} (or serializing the AttributeRef
* to JSON) will produce the original string. If validation fails, {@link #getError()} will
* return a non-null error and any SDK method that takes this AttributeRef as a parameter
* will consider it invalid.
*
* @param refPath an attribute name or path
* @return an AttributeRef
* @see #fromLiteral(String)
*/
public static AttributeRef fromPath(String refPath) {
if (refPath == null || refPath.isEmpty() || refPath.equals("/")) {
return new AttributeRef(Errors.ATTR_EMPTY, refPath);
}
if (refPath.charAt(0) != '/') {
// When there is no leading slash, this is a simple attribute reference with no character escaping.
return new AttributeRef(refPath, refPath, null);
}
if (refPath.indexOf('/', 1) < 0) {
// There's only one segment, so this is still a simple attribute reference. However, we still may
// need to unescape special characters.
String unescaped = unescapePath(refPath.substring(1));
if (unescaped == null) {
return new AttributeRef(Errors.ATTR_INVALID_ESCAPE, refPath);
}
return new AttributeRef(refPath, unescaped, null);
}
if (refPath.endsWith("/")) {
// String.split won't behave properly in this case
return new AttributeRef(Errors.ATTR_EXTRA_SLASH, refPath);
}
String[] parsed = refPath.substring(1).split("/");
for (int i = 0; i < parsed.length; i++) {
String p = parsed[i];
if (p.isEmpty()) {
return new AttributeRef(Errors.ATTR_EXTRA_SLASH, refPath);
}
String unescaped = unescapePath(p);
if (unescaped == null) {
return new AttributeRef(Errors.ATTR_INVALID_ESCAPE, refPath);
}
parsed[i] = unescaped;
}
return new AttributeRef(refPath, null, parsed);
}

/**
* Similar to {@link #fromPath(String)}, except that it always interprets the string as a literal
* attribute name, never as a slash-delimited path expression.
* <p>
* There is no escaping or unescaping, even if the name contains literal '/' or '~' characters.
* Since an attribute name can contain any characters, this method always returns a valid
* AttributeRef unless the name is empty.
* <p>
* For example: {@code AttributeRef.fromLiteral("name")} is exactly equivalent to
* {@code AttributeRef.fromPath("name")}. {@code AttributeRef.fromLiteral("a/b")} is exactly
* equivalent to {@code AttributeRef.fromPath("a/b")} (since the syntax used by
* {@link #fromPath(String)} treats the whole string as a literal as long as it does not start
* with a slash), or to {@code AttributeRef.fromPath("/a~1b")}.
*
* @param attributeName an attribute name
* @return an AttributeRef
* @see #fromPath(String)
*/
public static AttributeRef fromLiteral(String attributeName) {
if (attributeName == null || attributeName.isEmpty()) {
return new AttributeRef(Errors.ATTR_EMPTY, "");
}
if (attributeName.charAt(0) != '/') {
// When there is no leading slash, this is a simple attribute reference with no character escaping.
AttributeRef internedInstance = COMMON_LITERALS.get(attributeName);
return internedInstance == null ? new AttributeRef(attributeName, attributeName, null) : internedInstance;
}
// If there is a leading slash, then the attribute name actually starts with a slash. To represent it
// as an AttributeRef, it'll need to be escaped.
String escapedPath = "/" + attributeName.replace("~", "~0").replace("/", "~1");
return new AttributeRef(escapedPath, attributeName, null);
}

/**
* True for a valid AttributeRef, false for an invalid AttributeRef.
* <p>
* An AttributeRef can only be invalid for the following reasons:
* <ul>
* <li> The input string was empty, or consisted only of "/". </li>
* <li> A slash-delimited string had a double slash causing one component to be empty, such as "/a//b". </li>
* <li> A slash-delimited string contained a "~" character that was not followed by "0" or "1". </li>
* </ul>
* <p>
* Otherwise, the AttributeRef is valid, but that does not guarantee that such an attribute exists
* in any given {@link LDContext}. For instance, {@code fromLiteral("name")} is a valid AttributeRef,
* but a specific {@link LDContext} might or might not have a name.
* <p>
* See comments on the {@link AttributeRef} type for more details of the attribute reference synax.
*
* @return true if the instance is valid
* @see #getError()
*/
public boolean isValid() {
return error == null;
}

/**
* Null for a valid AttributeRef, or a non-null error message for an invalid AttributeRef.
* <p>
* If this is null, then {@link #isValid()} is true. If it is non-null, then {@link #isValid()} is false.
*
* @return an error string or null
* @see #isValid()
*/
public String getError() {
return error;
}

/**
* The number of path components in the AttributeRef.
* <p>
* For a simple attribute reference such as "name" with no leading slash, this returns 1.
* <p>
* For an attribute reference with a leading slash, it is the number of slash-delimited path
* components after the initial slash. For instance, {@code AttributeRef.fromPath("/a/b").getDepth()}
* returns 2.
* <p>
* For an invalid attribute reference, it returns zero
*
* @return the number of path components
*/
public int getDepth() {
if (error != null) {
return 0;
}
return components == null ? 1 : components.length;
}

/**
* Retrieves a single path component from the attribute reference.
* <p>
* For a simple attribute reference such as "name" with no leading slash, getComponent returns the
* attribute name if index is zero, and null otherwise.
* <p>
* For an attribute reference with a leading slash, if index is non-negative and less than
* {@link #getDepth()}, getComponent returns the path component string at that position.
*
* @param index the zero-based index of the desired path component
* @return the path component, or null if not available
*/
public String getComponent(int index) {
if (components == null) {
return index == 0 ? singlePathComponent : null;
}
return index < 0 || index >= components.length ? null : components[index];
}

/**
* Returns the attribute reference as a string, in the same format used by
* {@link #fromPath(String)}.
* <p>
* If the AttributeRef was created with {@link #fromPath(String)}, this value is identical to
* to the original string. If it was created with {@link #fromLiteral(String)}, the value may
* be different due to unescaping (for instance, an attribute whose name is "/a" would be
* represented as "~1a").
*
* @return the attribute reference string (guaranteed non-null)
*/
@Override
public String toString() {
return rawPath;
}

@Override
public boolean equals(Object other) {
if (other instanceof AttributeRef) {
AttributeRef o = (AttributeRef)other;
return rawPath.equals(o.rawPath);
}
return false;
}

@Override
public int hashCode() {
return rawPath.hashCode();
}

@Override
public int compareTo(AttributeRef o) {
return rawPath.compareTo(o.rawPath);
}

private static String unescapePath(String path) {
// If there are no tildes then there's definitely nothing to do
if (path.indexOf('~') < 0) {
return path;
}
StringBuilder ret = new StringBuilder(100); // arbitrary initial capacity
for (int i = 0; i < path.length(); i++) {
char ch = path.charAt(i);
if (ch != '~')
{
ret.append(ch);
continue;
}
i++;
if (i >= path.length())
{
return null;
}
switch (path.charAt(i)) {
case '0':
ret.append('~');
break;
case '1':
ret.append('/');
break;
default:
return null;
}
}
return ret.toString();
}

private static Map<String, AttributeRef> makeLiteralsMap(String... names) {
Map<String, AttributeRef> ret = new HashMap<>();
for (String name: names) {
ret.put(name, new AttributeRef(name, name, null));
}
return ret;
}
}
19 changes: 19 additions & 0 deletions src/main/java/com/launchdarkly/sdk/AttributeRefTypeAdapter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.launchdarkly.sdk;

import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;

final class AttributeRefTypeAdapter extends TypeAdapter<AttributeRef> {
@Override
public AttributeRef read(JsonReader reader) throws IOException {
return AttributeRef.fromPath(Helpers.readNonNullableString(reader));
}

@Override
public void write(JsonWriter writer, AttributeRef a) throws IOException {
writer.value(a.toString());
}
}
Loading