/*
* Copyright 2013, L. Murer.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see < http://www.gnu.org/licenses/ >.
*/
package controllers;
import actions.MailAction;
import de.schildbach.pte.dto.Connection;
import models.ComplexRequests;
import models.Request;
import play.Logger;
import play.api.templates.Html;
import play.mvc.Controller;
import play.mvc.Http;
import play.mvc.Result;
import play.mvc.With;
import tags.FastTags;
import javax.annotation.Nullable;
import javax.validation.constraints.NotNull;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Application extends Controller {
/**
* Shows a webpage to the user, that displays some input fields to start a request. There may be some predefined values
*/
@With(MailAction.class)
public static Result index() {
Collection<String> errors = new HashSet<>();
Request request = getGetRequest(errors);
Result redirectOldRequests = redirectOldRequests();
if (redirectOldRequests != null) {
return redirectOldRequests;
}
return index(request, errors);
}
/**
* Shows a webpage to the user, that displays some input fields to start a request. There may be some predefined values
* //@param provider the ID of the provider to get the timetable from
*/
private static Status index(@NotNull Request request, @NotNull Collection<String> errors) {
return ok(views.html.Application.index.render(request, errors));
}
/**
* Gets the current GET-Request with corrected Array-Numbers
*
* @return The current GET-Request
*/
private static Request getGetRequest(Collection<String> errors) {
final Http.Request httpRequest = request();
final Map<String, String[]> gets = new TreeMap<>(httpRequest.queryString());
final Map<String, String> get = new TreeMap<>();
final Map<String, Integer> occurrences = new HashMap<>(gets.size());
final Pattern array_delimiter = Pattern.compile("(.*)\\[\\]");
//Add fix values
get.put("provider", "sbb");
final Matcher httpRequestMatcher = Pattern.compile("[^\\?]*\\?(.*)").matcher(httpRequest.uri());
if (httpRequestMatcher.matches()) {
get.put("requestString", httpRequestMatcher.group(1));
}
//prepare each GET with the correct [\d]
for (String key : gets.keySet()) {
final String[] value = gets.get(key);
final Matcher key_match = array_delimiter.matcher(key);
if (key_match.matches()) {
key = key_match.group(1);
if (value == null || value.length == 0) {
get.put(key, null);
} else {
for (String v : value) {
int occurrence;
if (occurrences.containsKey(key)) {
occurrence = occurrences.get(key) + 1;
} else {
occurrence = 0;
}
final String key_array_name = key + "[" + occurrence + "]";
while (gets.keySet().contains(key_array_name)) {
occurrence++;
}
occurrences.put(key, occurrence);
get.put(key_array_name, v);
}
}
} else {
String value_to_put = null;
if (value != null && value.length > 0) {
value_to_put = value[0];
}
get.put(key, value_to_put);
}
}
return processGETvalues(get, errors);
}
protected static Request processGETvalues(Map<String, String> get, Collection<String> errors) {
final Request request = myBind(get, errors);
while (request.from.size() < 2) {
request.from.add("");
}
while (request.to.size() < 2) {
request.to.add("");
}
if (request().uri().length() > 1) {
errors.addAll(request.validate());
}
return request;
}
private static Request myBind(Map<String, String> get, Collection<String> errors) {
final Request ret = new Request();
for (String key : get.keySet()) {
try {
final String value = get.get(key);
final Matcher matcher = Pattern.compile("(.)(.*)\\[\\d*\\]").matcher(key);
if (matcher.matches()) {
Request.class.getDeclaredMethod("add" + matcher.group(1).toUpperCase() + matcher.group(2), String.class).invoke(ret, value);
} else {
Method method = null;
for (Method m : Request.class.getDeclaredMethods()) {
if (m.getParameterTypes().length == 1 &&
m.getParameterTypes()[0] == String.class &&
("set" + key).toLowerCase().equals(m.getName().toLowerCase())) {
method = m;
break;
}
}
if (method == null) {
Request.class.getDeclaredField(key).set(ret, value);
} else {
method.invoke(ret, value);
}
}
} catch (Exception e) {
Logger.error("Unbekannter Parameter '" + key + "'.", e);
errors.add("Unbekannter Parameter '" + key + "'.");
}
}
return ret;
}
private static Result redirectOldRequests() {
String uri = request().uri();
if (uri.contains("start[") || uri.contains("stop[") || uri.contains("direkt[") || uri.contains("start%5B") || uri.contains("stop%5B") || uri.contains("direkt%5B")) {
String newUri = uri.replace("start", "from").replace("stop", "to").replace("direkt", "direct");
Logger.warn("Forwarded " + uri);
Logger.warn(" to " + newUri);
return movedPermanently(newUri);
} else {
return null;
}
}
/**
* Shows a webpage with some timetables to the user, depending on the given parameters
*/
@With(MailAction.class)
public static Result showTimetable() {
Result redirectOldRequests = redirectOldRequests();
if (redirectOldRequests != null) {
return redirectOldRequests;
}
Set<String> errors = new HashSet<>();
Request request = getGetRequest(errors);
if (!errors.isEmpty()) {
return index(request, errors);
}
return getTimetable(request, errors);
}
protected static Result getTimetable(Request request, Set<String> errors) {
try {
List<String> warnings = new ArrayList<>();
List<Connection> connections = ComplexRequests.getMultipleTimetables(request, warnings);
return ok(views.html.Application.showTimetable.render(request, connections, warnings, new FastTags.IteratorExtender()));
} catch (ComplexRequests.ServerNotReachableException e) {
errors.add(e.getLocalizedMessage());
return index(request, errors);
}
}
/**
* "Moves permanently" the GET into a request without trailing slash
*
* @param path String
* @return Result
*/
@With(MailAction.class)
public static Result redirectUntrailed(@SuppressWarnings("UnusedParameters") String path) {
String uri = request().uri();
if (uri.contains("/?")) {
uri = uri.replaceFirst("/?", "?");
} else if (uri.endsWith("/")) {
uri = uri.substring(0, uri.length() - 1);
}
return movedPermanently(uri);
}
@Nullable
public static Html getErrorPage(@NotNull Throwable e) {
Html result;
if (Math.random() > 0.03) { //show almost always, but prevent from a loop
Collection<String> errors = new HashSet<>();
errors.add("Ein internet Fehler ist aufgetreten. Bitte mit anderen Daten oder später nochmals versuchen. <br><span style=\"font-size:smaller\">(<b>Error:</b> " + e.getLocalizedMessage() + ")</span>");
result = views.html.Application.index.render(new Request(), errors);
} else {
result = null;
}
return result;
}
public static Result overlookQuery(@SuppressWarnings("UnusedParameters") String anything) {
return ok();
}
}