Skip to content

Commit

Permalink
Merge pull request ResearchKit#14 from Robert-Kolmos/step-gson-testing
Browse files Browse the repository at this point in the history
Port iOS step object implementations
  • Loading branch information
liujoshua committed Apr 30, 2018
2 parents 6e2a17d + cf1ada5 commit d6ec1d1
Show file tree
Hide file tree
Showing 25 changed files with 876 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
package org.sagebionetworks.research.app;

import org.junit.*;
import org.sagebionetworks.research.domain.step.ActiveUIStep;
import org.sagebionetworks.research.domain.step.ActiveUIStepBase;
import org.sagebionetworks.research.domain.step.Step;

import static org.junit.Assert.*;
Expand All @@ -53,7 +53,7 @@ public void testActiveUIStep() {

Step step = stepTestComponent.gson().fromJson("{'identifier':'stepId', 'type': 'active'}", Step.class);

assertTrue(step instanceof ActiveUIStep);
assertTrue(step instanceof ActiveUIStepBase);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,111 @@

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;

import org.sagebionetworks.research.domain.RuntimeTypeAdapterFactory;
import org.sagebionetworks.research.domain.step.ActiveUIStepBase;
import org.sagebionetworks.research.domain.step.StepType;
import org.sagebionetworks.research.domain.step.ui.ActiveUIStep;
import org.threeten.bp.Duration;

import java.lang.reflect.Type;
import java.util.Map;
import java.util.Set;

import dagger.MapKey;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
import dagger.multibindings.Multibinds;
import javax.inject.Inject;
import javax.inject.Singleton;

@Module
public abstract class GsonModule {

@Multibinds
abstract Map<Class, JsonDeserializer> jsonDeserializerMap();
@MapKey
public @interface ClassKey{
Class value();
}

/**
* @return The json Deserializer for an active step.
*/
@Provides
@IntoMap
@ClassKey(ActiveUIStepBase.class)
static JsonDeserializer provideActiveUIStep() {
return new JsonDeserializer<ActiveUIStep>() {
@Override
public ActiveUIStep deserialize(final JsonElement json, final Type typeOfT,
final JsonDeserializationContext context)
throws JsonParseException {
if (json.isJsonObject()) {
JsonObject object = json.getAsJsonObject();
String identifier = getStringFieldNonNull(object, "identifier");
String title = getStringFieldNullable(object, "title");
String text = getStringFieldNullable(object, "text");
String detail = getStringFieldNullable(object, "detail");
String footnote = getStringFieldNullable(object, "footnote");
Duration duration = null;
JsonElement durationElement = object.get("duration");
if (durationElement != null) {
if (!durationElement.isJsonPrimitive()) {
throw new JsonParseException("duration " + durationElement.toString() + " should be an integer");
}
int durationInSeconds = durationElement.getAsInt();
duration = Duration.ofSeconds(durationInSeconds);
}

return new ActiveUIStepBase(identifier, title, text, detail, footnote,
duration, false);
}

throw new JsonParseException("json " + json.toString() + "is not an object");
}
};
}

/**
* Returns the string from the given field or null if the given field has been ommited from the JSON
* @param json the json object to get the field from
* @param key the name of the field to get from the json object
* @return The string that corresponds to key or null if no such String exists
*/
private static String getStringFieldNullable(JsonObject json, String key) {
JsonElement element = json.get(key);
if (element != null) {
return element.getAsString();
}

return null;
}

/**
* Returns the string corresponding to the given key in the given json object, or throws a
* JsonParseExecption if no such String exists.
* @param json The json to get the string field from.
* @param key The field to get as a string from the given json.
* @return The string corresponding to the given key in the given json object.
* @throws JsonParseException if there is no string corresponding to the given key in the json object.
*/
private static String getStringFieldNonNull(JsonObject json, String key) throws JsonParseException {
JsonElement element = json.get(key);
if (element != null) {
String result = element.getAsString();
if (result != null) {
return result;
}
}

throw new JsonParseException("NonNull field " + key + "of object " + json.toString() + "couldn't be parsed");
}

@Provides
@Singleton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@
package org.sagebionetworks.research.domain.inject;

import org.sagebionetworks.research.domain.RuntimeTypeAdapterFactory;
import org.sagebionetworks.research.domain.step.ActiveUIStep;
import org.sagebionetworks.research.domain.step.ActiveUIStepBase;
import org.sagebionetworks.research.domain.step.SectionStep;
import org.sagebionetworks.research.domain.step.SectionStepBase;
import org.sagebionetworks.research.domain.step.Step;
import org.sagebionetworks.research.domain.step.UIStepBase;

import java.util.Map;
import java.util.Map.Entry;
Expand All @@ -44,6 +47,7 @@
import dagger.Provides;
import dagger.multibindings.IntoMap;
import dagger.multibindings.IntoSet;
import dagger.multibindings.Multibinds;

@Module(includes = {GsonModule.class})
public class StepModule {
Expand All @@ -57,13 +61,33 @@ public class StepModule {
}

/**
* @return json type key for ActiveUIStep.class
* @return json type key for ActiveUIStepBase.class
*/
@Provides
@IntoMap
@StepClassKey(ActiveUIStep.class)
@StepClassKey(ActiveUIStepBase.class)
static String provideActiveUIStep() {
return ActiveUIStep.TYPE_KEY;
return ActiveUIStepBase.TYPE_KEY;
}

/**
* @return json type key for UIStepBase.class
*/
@Provides
@IntoMap
@StepClassKey(UIStepBase.class)
static String provideUIStep() {
return UIStepBase.TYPE_KEY;
}

/**
* @return json type key for SectionStepBase.class
*/
@Provides
@IntoMap
@StepClassKey(SectionStepBase.class)
static String provideSectionStep() {
return SectionStepBase.TYPE_KEY;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* BSD 3-Clause License
*
* Copyright 2018 Sage Bionetworks. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. Neither the name of the copyright holder(s) nor the names of any contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission. No license is granted to the trademarks of
* the copyright holders even if such marks are included in this software.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package org.sagebionetworks.research.domain.step;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.base.Objects;

import org.sagebionetworks.research.domain.step.ui.ActiveUIStep;
import org.threeten.bp.Duration;


public class ActiveUIStepBase extends UIStepBase implements ActiveUIStep {
public static final String TYPE_KEY = "active";

@Nullable
private final Duration duration;
private final boolean backgroundAudioRequired;

public ActiveUIStepBase(@NonNull final String identifier,
@Nullable final String title, @Nullable final String text,
@Nullable final String detail, @Nullable final String footnote,
@Nullable final Duration duration, final boolean backgroundAudioRequired) {
super(identifier, title, text, detail, footnote);
this.duration = duration;
this.backgroundAudioRequired = backgroundAudioRequired;
}
@Nullable
@Override
public Duration getDuration() {
return this.duration;
}

@NonNull
@Override
public String getType() {
return TYPE_KEY;
}

@Override
public boolean isBackgroundAudioRequired() {
return this.backgroundAudioRequired;
}

@Override
public int hashCode() {
return super.hashCode() + 3 * Objects.hashCode(this.duration, this.backgroundAudioRequired);
}

/**
* Override equalsHelepr to also check the equality of Duration and isBackgroundAudioRequired.
* @param o The object to check for equality with this.
* @return True if this is equal to other, false otherwise.
*/
@Override
protected boolean equalsHelper(Object o) {
ActiveUIStepBase activeStep = (ActiveUIStepBase) o;
return super.equalsHelper(o) &&
Objects.equal(this.getDuration(), activeStep.getDuration()) &&
this.isBackgroundAudioRequired() == activeStep.isBackgroundAudioRequired();
}

/**
* Override toStringHelper to add the duration and isBackgroundAudioRequired fields to the
* ToStringHelper.
* @return The ToStringHelper that can create a String representation of this.
*/
@Override
protected ToStringHelper toStringHelper() {
return super.toStringHelper()
.add("duration", this.getDuration())
.add("isBackgroundAudioRequired", this.isBackgroundAudioRequired());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* BSD 3-Clause License
*
* Copyright 2018 Sage Bionetworks. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. Neither the name of the copyright holder(s) nor the names of any contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission. No license is granted to the trademarks of
* the copyright holders even if such marks are included in this software.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package org.sagebionetworks.research.domain.step;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.base.Objects;

import org.sagebionetworks.research.domain.form.InputField;
import org.sagebionetworks.research.domain.step.ui.FormUIStep;

import java.util.List;

public class FormUIStepBase extends UIStepBase implements FormUIStep {
@NonNull
private final List<InputField> inputFields;

public static final String TYPE_KEY = "form";

public FormUIStepBase(@NonNull final String identifier, @Nullable final String title,
@Nullable final String text, @Nullable final String detail, @Nullable final String footnote,
@NonNull final List<InputField> inputFields) {
super(identifier, title, text, detail, footnote);
this.inputFields = inputFields;
}

@NonNull
@Override
public List<InputField> getInputFields() {
return this.inputFields;
}

@NonNull
@Override
public String getType() {
return TYPE_KEY;
}

@Override
public int hashCode() {
return super.hashCode() + 3 * Objects.hashCode(this.inputFields);
}

/**
* Override equalsHelper to also compare the input fields.
* @param o The object to check for equality with this.
* @return True if this is equal to o, false otherwise.
*/
@Override
protected boolean equalsHelper(Object o) {
FormUIStepBase formStep = (FormUIStepBase) o;
return super.equalsHelper(o) &&
Objects.equal(this.getInputFields(), formStep.getInputFields());
}

/**
* Override toStringHelper to also add the input fields.
* @return The ToStringHelper that can produce the toString for this.
*/
@Override
protected ToStringHelper toStringHelper() {
return super.toStringHelper()
.add("inputFields", this.getInputFields());
}
}

0 comments on commit d6ec1d1

Please sign in to comment.