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.
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
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
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
@@ -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());
}
}
@@ -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.