All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fr.faylixe.googlecodejam.client.webservice.Problem Maven / Gradle / Ivy

There is a newer version: 1.4.11
Show newest version
package fr.faylixe.googlecodejam.client.webservice;

import fr.faylixe.googlecodejam.client.common.HTMLConstant;
import fr.faylixe.googlecodejam.client.common.NamedObject;
import fr.faylixe.googlecodejam.client.common.Resources;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectInputValidation;
import java.io.OptionalDataException;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import com.google.gson.Gson;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;

/**
 * 

POJO that aims to be bind to the /ContestInfo * request, using Gson API. {@link Problem} instance belong * to a {@link ContestInfo} object, and consists in the problem * metadata such a name, description, and IO details.

* * @author fv */ public final class Problem extends NamedObject implements ObjectInputValidation { /** Serialization index. **/ private static final long serialVersionUID = 1L; /** Full HTML text that describes this problem. **/ @SerializedName("body") private String body; /** Problem unique identifier. **/ @SerializedName("id") private String id; /** TODO : Figure out what is key for. **/ @SerializedName("key") private String key; /** TODO : Figure out what is type for. **/ @SerializedName("type") private String type; /** List of inputs that are available for solving in this problem. **/ @SerializedName("io") private ProblemInput [] inputs; /** Parent contest of this problem. **/ private transient ContestInfo parent; /** Normalized name generated by {@link Resources#normalize(String)} method. **/ private String normalizedName; /** * Custom deserializer that normalizes problem body content. * * @author fv */ public static class Deserializer implements JsonDeserializer { /** Target hostname problem are extracted from. **/ private final String hostname; /** * Default constructor. * * @param hostname Target hostname problem are extracted from. */ protected Deserializer(final String hostname) { this.hostname = hostname; } /** {@inheritDoc} **/ @Override public Problem deserialize( final JsonElement element, final Type type, final JsonDeserializationContext context) throws JsonParseException { final Gson parser = new Gson(); final Problem problem = parser.fromJson(element, Problem.class); final String normalized = normalize(problem.body); try { problem.body = String.format(Resources.getHTMLTemplate(), normalized); } catch (final IOException e) { throw new JsonParseException(e); } problem.normalizedName = Resources.normalize(problem.getName()); for (final ProblemInput input : problem.getProblemInputs()) { input.setParent(problem); } return problem; } /** * Normalizes the given HTML body text, by replacing * images URI by absolute URI using preference hostname. * * @param body HTML body to normalize. * @return Normalized HTML content. */ private String normalize(final String body) { final Document document = Jsoup.parse(body); final Elements images = document.getElementsByTag(HTMLConstant.IMG); final StringBuilder builder = new StringBuilder(); for (final Element image : images) { final String original = image.attr(HTMLConstant.SRC); if (!original.startsWith("https://")) { builder .append(hostname.charAt(0) == '/' ? hostname.substring(0, -1) : hostname) .append('/') .append(original.charAt(0) == '/' ? original.substring(1) : original); image.attr(HTMLConstant.SRC, builder.toString()); builder.delete(0, builder.length()); } } return document.html(); } } /** * Contest setter that aims to be called by {@link ContestInfo} static factory. * * @param parent Parent contest of this problem. */ protected void setParent(final ContestInfo parent) { this.parent = parent; } /** * Getter for the parent contest of this problem. * * @return Parent contest of this problem. * @see #parent */ public ContestInfo getParent() { return parent; } /** * Getter for the problem normalized name. * * @return Normalized name generated by {@link Resources#normalize(String)} method. * @see #normalizedName */ public String getNormalizedName() { return normalizedName; } /** * Getter for the problem body description. * * @return Full HTML text that describes this problem. * @see #body */ public String getBody() { return body; } /** * Getter for the problem id. * * @return Problem unique identifier. * @see #id */ public String getId() { return id; } /** * Getter for the problem key. * @return TODO : Figure out what is key for. * @see #key */ public String getKey() { return key; } /** * Getter for the problem type. * * @return TODO : Figure out what is type for. * @see #type */ public String getType() { return type; } /** * Getter for the problem inputs. * * @return List of inputs that are available for solving in this problem. * @see #inputs */ public List getProblemInputs() { return (inputs == null ? Collections.emptyList() : Arrays.asList(inputs)); } /** * Shortcut method for reducing law of Demeters issues. * * @param index Index of the problem input to retrieve. * @return Problem input instance required. * @throws ArrayIndexOutOfBoundsException If the given index is not valid. */ public ProblemInput getProblemInput(final int index) { final List inputs = getProblemInputs(); if (index < 0 || inputs.size() <= index) { throw new ArrayIndexOutOfBoundsException(); } return inputs.get(index); } /** * Filters and returns first problem input which name * match the given type * * @param type Type of the input to retrieve (usually small or large). * @return Corresponding input if any, null otherwise. */ public ProblemInput getProblemInput(final String type) { final String name = type.toLowerCase(); for (final ProblemInput input : getProblemInputs()) { if (input.getName().equals(name)) { return input; } } return null; } /** {@inheritDoc} **/ @Override public void validateObject() throws InvalidObjectException { for (final ProblemInput input : getProblemInputs()) { input.setParent(this); } } /** * Custom readObject method that registers this object as a deserialization validator. * * @param stream {@link ObjectInputStream} to register this validator to. * @throws OptionalDataException If any error occurs while reading the object. * @throws ClassNotFoundException If the default readObject call can not find a required class. * @throws IOException If any error occurs while reading the object. */ private void readObject(final ObjectInputStream stream) throws OptionalDataException, ClassNotFoundException, IOException { stream.registerValidation(this, 0); stream.defaultReadObject(); } /** {@inheritDoc} **/ @Override public int hashCode() { return id.hashCode(); } /** {@inheritDoc} **/ @Override public boolean equals(final Object object) { if (object == this) { return true; } if (object == null || object.getClass() != getClass()) { return false; } final Problem other = (Problem) object; return id.equals(other.getId()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy