Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
29 changes: 20 additions & 9 deletions cwms-data-api/src/main/java/cwms/cda/formatters/ContentType.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
import java.util.Map;
import java.util.Objects;

/**
* Stores an instance of ContentType and it's parameters.
* Example <pre>application/json;q=1</pre> is different than <pre>application/json;q=2</pre>
*/
public class ContentType implements Comparable<ContentType> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
public static final String PARAM_DELIM = ";";
Expand All @@ -13,8 +17,13 @@ public class ContentType implements Comparable<ContentType> {
private final String mediaType;
private final Map<String, String> parameters;

private String charset = null;
private String instanceCharset = null;

/**
* Create a ContentType instance given the provide ContentType or Accept Header.
* The contructor will parse query parameters out of the provided string.
* @param contentTypeHeader provided ContentType or Accept header text
*/
public ContentType(String contentTypeHeader) {
parameters = new LinkedHashMap<>();
String[] parts = contentTypeHeader.split(PARAM_DELIM);
Expand All @@ -26,7 +35,7 @@ public ContentType(String contentTypeHeader) {
String key = keyVal[0].trim();
String value = keyVal[1].trim();
if (CHARSET.equalsIgnoreCase(key)) {
charset = value;
instanceCharset = value;
} else {
parameters.put(key, value);
}
Expand All @@ -43,17 +52,19 @@ public Map<String, String> getParameters() {
return new LinkedHashMap<>(parameters);
}

public String getCharset() {
return charset;
public String getInstanceCharset() {
return instanceCharset;
Comment thread
krowvin marked this conversation as resolved.
}

/**
* For the purposes of cwms-data-api content-type equals we only care about the following
* fields matching:
*
* fields matching.
*
* <p>
* - the mimetype itself
* - the version parameter
*
*
* <p>
* For us everything else is informational or used indirectly
*/
@Override
Expand All @@ -67,7 +78,7 @@ public boolean equals(Object other) {
return false;
}

/** We loop through instead of using contains key.
/* We loop through instead of using contains key.
* Content-type parameter names are not case sensitive.
*/
for (Map.Entry<String, String> entry : parameters.entrySet()) {
Expand Down Expand Up @@ -116,7 +127,7 @@ public String toString() {
/**
* Used for quick comparisons where we don't further need the content type
* so we can streamline the code a little.
*
*
* @param a first content type to check
* @param b second content type to check
* @return whether they are equivalent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,15 @@

import cwms.cda.data.dto.CwmsDTOBase;
import cwms.cda.formatters.annotations.FormattableWith;
import org.jetbrains.annotations.NotNull;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.jetbrains.annotations.NotNull;

final class ContentTypeAliasMap {
private final Map<ContentType, ContentType> contentTypeMap = new ConcurrentHashMap<>();
private static final Map<Class<? extends CwmsDTOBase>, ContentTypeAliasMap> ALIAS_MAP = new ConcurrentHashMap<>();

private ContentTypeAliasMap()
{
private ContentTypeAliasMap() {
}

private ContentTypeAliasMap(Class<? extends CwmsDTOBase> dtoClass) {
Expand Down
104 changes: 82 additions & 22 deletions cwms-data-api/src/main/java/cwms/cda/formatters/Formats.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,18 @@

package cwms.cda.formatters;

import com.google.common.flogger.FluentLogger;
import cwms.cda.data.dto.CwmsDTOBase;
import cwms.cda.formatters.annotations.FormattableWith;

import java.util.SortedSet;
import java.util.TreeSet;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import com.google.common.flogger.FluentLogger;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -96,13 +95,19 @@ public class Formats {
}


private final Map<ContentType, Map<Class<? extends CwmsDTOBase>, OutputFormatter>> formatters = new LinkedHashMap<>();
private final Map<ContentType, Map<Class<? extends CwmsDTOBase>, OutputFormatter>> formatters =
new LinkedHashMap<>();

private static final Formats formats = new Formats();

private Formats() {
}

/**
* Given the provided content type, get the appropriate legacy content-type.
* @param contentType given ContentType
* @return a previously configured contenttype value, or the value of {@link JSON_LEGACY}
*/
public static String getLegacyTypeFromContentType(ContentType contentType) {
return typeMap.entrySet()
.stream()
Expand Down Expand Up @@ -172,8 +177,8 @@ private <T extends CwmsDTOBase> T parseContentFromType(ContentType type, InputSt
}
}

private <T extends CwmsDTOBase> List<T> parseContentListFromType(ContentType type, String content, Class<T> rootType)
throws FormattingException {
private <T extends CwmsDTOBase> List<T> parseContentListFromType(ContentType type, String content,
Class<T> rootType) throws FormattingException {
OutputFormatter outputFormatter = getOutputFormatterInternal(type, rootType);
if (outputFormatter != null) {
List<T> retval = outputFormatter.parseContentList(content, rootType);
Expand Down Expand Up @@ -218,29 +223,78 @@ private OutputFormatter getOutputFormatterInternal(ContentType type,
return outputFormatter;
}

/**
* Retrieve the appropriate OutputFormatter for the given ContentType and DTO Class.
* @param ct ContentType desired
* @param klass CwmsDto
* @return Appropriate formatter for the given ContentType and klass
*/
public static OutputFormatter getOutputFormatter(ContentType ct, Class<? extends CwmsDTOBase> klass) {
return formats.getOutputFormatterInternal(ct, klass);
return formats.getOutputFormatterInternal(ct, klass);
}

/**
* Retrieve the formatted output for the given ContentType and DTO Instance.
* @param type ContentType Desired
* @param toFormat Instance to format
* @return String containing text of the DTO in the appropriate format
* @throws FormattingException issues with Formatter lookup or formatting.
*/
public static String format(ContentType type, CwmsDTOBase toFormat) throws FormattingException {
return formats.getFormatted(type, toFormat);
}

public static String format(ContentType type, List<? extends CwmsDTOBase> toFormat, Class<?
extends CwmsDTOBase> rootType) throws FormattingException {
/**
* Retrieve the formatted output for the given ContentType and DTO Instances and a DTO Type.
* @param type content type desired
* @param toFormat list of objects to format
* @param rootType DTO type of the list members
* @return Formatted String
* @throws FormattingException if the list of objects could not be converted.
*/
public static String format(ContentType type, List<? extends CwmsDTOBase> toFormat,
Class<? extends CwmsDTOBase> rootType) throws FormattingException {
return formats.getFormatted(type, toFormat, rootType);
}

/**
* Given a ContentType, text, and a given DTO type, parse the text and return an Object instance of the DTO type.
* @param <T> DTO Type
* @param type ContentType of the input
* @param content data to parase
* @param rootType expected DTO type
* @return an instance of that DTO with the provided data.
* @throws FormattingException any issues with lookup of parser or parsing.
*/
public static <T extends CwmsDTOBase> T parseContent(ContentType type, String content, Class<T> rootType)
throws FormattingException {
return formats.parseContentFromType(type, content, rootType);
}

/**
* Given a ContentType, text, and a given DTO type, parse the text and return an Object instance of the DTO type.
* @param <T> DTO Type
* @param type ContentType of the input.
* @param inputStream source of data to parse.
* @param rootType expected DTO type.
* @return an instance of that DTO with the provided data.
* @throws FormattingException any issues with lookup of parser or parsing.
*/
public static <T extends CwmsDTOBase> T parseContent(ContentType type, InputStream inputStream, Class<T> rootType)
throws FormattingException {
return formats.parseContentFromType(type, inputStream, rootType);
}

/**
* Given a ContentType, text, and a given DTO type, parse the text and return a list of Object instance of the DTO
* type.
* @param <T> DTO Type
* @param type ContentType of the input
* @param content data to parase
* @param rootType expected DTO type
* @return an instance of that DTO with the provided data.
* @throws FormattingException any issues with lookup of parser or parsing.
*/
public static <T extends CwmsDTOBase> List<T> parseContentList(ContentType type, String content, Class<T> rootType)
throws FormattingException {
return formats.parseContentListFromType(type, content, rootType);
Expand All @@ -258,6 +312,7 @@ public static <T extends CwmsDTOBase> List<T> parseContentList(ContentType type,
* <code>FormattableWith</code> annotations.
* @return an appropriate standard mimetype for lookup
* @throws FormattingException if neither header nor queryParam can be parsed into a supported content type
* @throws UnsupportedFormatException if preconditions aren't met or format is not supported.
*/
public static ContentType parseHeaderAndQueryParm(String header, String queryParam, Class<? extends CwmsDTOBase> klass) {
// If a query parameter is provided, it overrides the header.
Expand All @@ -284,7 +339,8 @@ public static ContentType parseHeaderAndQueryParm(String header, String queryPar
* @return ContentType appropriate to the given selection.
* @throws UnsupportedFormatException if there is no matching content type for the given class
*/
public static ContentType parseQueryOrHeaderParam(String headerParam, String queryParam, Class<? extends CwmsDTOBase> klass) {
public static ContentType parseQueryOrHeaderParam(String headerParam, String queryParam,
Class<? extends CwmsDTOBase> klass) {
ContentType ct = null;
if (!(queryParam == null || queryParam.isEmpty())) {
ct = parseQueryParam(queryParam, klass);
Expand All @@ -294,27 +350,30 @@ public static ContentType parseQueryOrHeaderParam(String headerParam, String que
ct = parseHeader(DEFAULT, klass);
}
if (ct == null) {
throw new UnsupportedFormatException("Content-Type " + (headerParam == null ? queryParam : headerParam) + " is not available.");
throw new UnsupportedFormatException("Content-Type " + (headerParam == null ? queryParam : headerParam)
+ " is not available.");
}
return ct;
}

public static ContentType parseQueryParam(String queryParam, Class<? extends CwmsDTOBase> klass)
{
/**
* Given the ContentType provided in a queryParameter extract and convert to a ContentType issue.
* @param queryParam value of the "format" query parameter.
* @param klass type of DTO expected.
* @return instance of ContentType
*/
public static ContentType parseQueryParam(String queryParam, Class<? extends CwmsDTOBase> klass) {
ContentTypeAliasMap aliasMap = ContentTypeAliasMap.empty();
if (klass != null) {
aliasMap = ContentTypeAliasMap.forDtoClass(klass);
}

ContentType retVal = null;
if (queryParam != null && !queryParam.isEmpty())
{
if (queryParam != null && !queryParam.isEmpty()) {
String val = typeMap.get(queryParam);
if (val != null)
{
if (val != null) {
retVal = aliasMap.getContentType(val);
if (retVal == null)
{
if (retVal == null) {
retVal = new ContentType(val);
}
}
Expand All @@ -331,14 +390,15 @@ public static ContentType parseQueryParam(String queryParam, Class<? extends Cwm
* {@link cwms.cda.formatters.annotations.FormattableWith} annotations.
* @return an appropriate standard mimetype for lookup
* @throws FormattingException if the header can't be identified as a mimetype
* @throws UnsupportedFormatException if header is invalid or for a format that is not supported.
*/
public static @NotNull ContentType parseHeader(@Nullable String header,
@NotNull Class<? extends CwmsDTOBase> klass) {
Objects.requireNonNull(klass, "Cannot determine content type without a DTO class definition");
ContentTypeAliasMap aliasMap = ContentTypeAliasMap.forDtoClass(klass);
//Swap out null content type with */* for flexibility.
//This routine will match DTO's when the DEFAULT alias specified by the format annotations.
if(header == null || header.trim().isEmpty()) {
if (header == null || header.trim().isEmpty()) {
header = DEFAULT;
}
//TreeSet will sort based on prioritized content type
Expand All @@ -357,7 +417,7 @@ public static ContentType parseQueryParam(String queryParam, Class<? extends Cwm
//Only use the ContentType classes initialized in contentTypeList rather than
//the client headers itself
ContentType type = new ContentType(ct);
if(contentTypeList.contains(type)) {
if (contentTypeList.contains(type)) {
contentTypes.add(type);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ public FormattingException(String message) {
HttpServletResponse.SC_NOT_ACCEPTABLE, LOG_LEVEL, new HashMap<>(), null);
}

/**
* Formatting Exception with Message and Cause.
* @param message Additional message details.
* @param err specific cause of this exception.
*/
public FormattingException(String message, Throwable err) {
super(message, PARSER_SOURCE, "Formatting error:" + message,
((err instanceof IOException)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
package cwms.cda.formatters;

import java.util.List;

import cwms.cda.data.dto.LocationCategory;
import java.util.List;

public class LocationCategoryFormatV1
{
private final List<LocationCategory> locationCategories;
public class LocationCategoryFormatV1 {
private final List<LocationCategory> locationCategories;

public LocationCategoryFormatV1(List<LocationCategory> cats)
{
this.locationCategories = cats;
}
public LocationCategoryFormatV1(List<LocationCategory> cats) {
this.locationCategories = cats;
}

public List<LocationCategory> getLocationCategories()
{
return locationCategories;
}
public List<LocationCategory> getLocationCategories() {
return locationCategories;
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
package cwms.cda.formatters;

import cwms.cda.data.dto.LocationGroup;
import java.util.List;

import cwms.cda.data.dto.LocationGroup;

public class LocationGroupFormatV1
{
private List<LocationGroup> locationGroups;
public class LocationGroupFormatV1 {
private List<LocationGroup> locationGroups;

public LocationGroupFormatV1(List<LocationGroup> locationGroups)
{
this.locationGroups = locationGroups;
}
public LocationGroupFormatV1(List<LocationGroup> locationGroups) {
this.locationGroups = locationGroups;
}

public List<LocationGroup> getLocationGroups()
{
return locationGroups;
}
public List<LocationGroup> getLocationGroups() {
return locationGroups;
}

public void setLocationGroups(List<LocationGroup> locationGroups)
{
this.locationGroups = locationGroups;
}
public void setLocationGroups(List<LocationGroup> locationGroups) {
this.locationGroups = locationGroups;
}
}
Loading
Loading