/*
* Copyright (C) 2014 Stefan Niederhauser (nidin@gmx.ch)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package guru.nidi.ramltester.core;
import guru.nidi.ramltester.model.RamlMessage;
import guru.nidi.ramltester.util.InvalidMediaTypeException;
import guru.nidi.ramltester.util.MediaType;
import guru.nidi.ramltester.util.Message;
import org.raml.model.MimeType;
import java.util.*;
import static guru.nidi.ramltester.core.CheckerHelper.*;
/**
*
*/
final class MediaTypeMatch {
private final MediaType targetType;
private final Collection<MediaType> definedTypes;
private final MediaType matchingMedia;
private final MimeType matchingMime;
private MediaTypeMatch(MediaType targetType, Collection<MediaType> definedTypes, MediaType matchingMedia, MimeType matchingMime) {
this.targetType = targetType;
this.definedTypes = definedTypes;
this.matchingMedia = matchingMedia;
this.matchingMime = matchingMime;
}
public MediaType getTargetType() {
return targetType;
}
public Collection<MediaType> getDefinedTypes() {
return definedTypes;
}
public MediaType getMatchingMedia() {
return matchingMedia;
}
public MimeType getMatchingMime() {
return matchingMime;
}
public String getTargetCharset() {
return targetType.getCharset("iso-8859-1");
}
public static MediaTypeMatch find(RamlViolations violations, RamlMessage message, Map<String, MimeType> bodies, Locator locator) {
if (isNoOrEmptyBodies(bodies)) {
violations.addIf(hasContent(message), "body.superfluous", locator);
return null;
}
if (message.getContentType() == null) {
violations.addIf(hasContent(message) || !existSchemalessBody(bodies), "contentType.missing");
return null;
}
final MediaType targetType;
try {
targetType = MediaType.valueOf(message.getContentType());
} catch (InvalidMediaTypeException e) {
violations.add("mediaType.illegal", locator, message.getContentType(), e.getMessage());
return null;
}
final Map<MediaType, MimeType> mediaTypes = mediaTypes(violations, bodies, locator);
final List<Map.Entry<MediaType, MimeType>> bestMatches = findBestMatches(mediaTypes, targetType);
if (bestMatches.isEmpty()) {
violations.add("mediaType.undefined", locator, message.getContentType());
return null;
}
if (bestMatches.size() > 1) {
violations.add("mediaType.ambiguous", locator, new Locator(bestMatches.get(0).getValue()), new Locator(bestMatches.get(1).getValue()));
return null;
}
return new MediaTypeMatch(targetType, mediaTypes.keySet(), bestMatches.get(0).getKey(), bestMatches.get(0).getValue());
}
private static Map<MediaType, MimeType> mediaTypes(RamlViolations violations, Map<String, MimeType> bodies, Locator locator) {
final Map<MediaType, MimeType> types = new LinkedHashMap<>();
for (final Map.Entry<String, MimeType> entry : bodies.entrySet()) {
try {
types.put(MediaType.valueOf(entry.getKey()), entry.getValue());
} catch (InvalidMediaTypeException e) {
violations.add(new Message("mediaType.illegal", locator, entry.getKey(), e.getMessage()));
}
}
return types;
}
private static List<Map.Entry<MediaType, MimeType>> findBestMatches(Map<MediaType, MimeType> types, MediaType targetType) {
final List<Map.Entry<MediaType, MimeType>> bestMatches = new ArrayList<>();
for (final Map.Entry<MediaType, MimeType> entry : types.entrySet()) {
final int similarity = targetType.similarity(entry.getKey());
if (bestMatches.isEmpty()) {
if (similarity > 0) {
bestMatches.add(entry);
}
} else {
final int bestSimilarity = targetType.similarity(bestMatches.get(0).getKey());
if (similarity > bestSimilarity) {
bestMatches.clear();
bestMatches.add(entry);
} else if (similarity == bestSimilarity) {
bestMatches.add(entry);
}
}
}
return bestMatches;
}
}