// Last edited on 2014-12-17 03:46:15 by stolfilocal package quack; import java.util.List; import java.util.Date; import java.util.TimeZone; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.text.ParseException; import java.io.PrintWriter; import java.io.IOException; import javax.servlet.http.HttpServletResponse; public class HTMLToolsImpl implements HTMLTools { private Server server = null; // Server, for basic stats, clock, etc. private DateFormat timestampConverter = null; // Formatador para datahoras (menos fuso horário). private static final String UTC_TIMEZONE = "UTC"; // Código do fuso horário UTC (= GMT, horário de Greenwich, sem horário de verão). private static final String STANDARD_TIMESTAMP_FORMAT = "yyyy-mm-dd HH:MM:SS"; // Formato externo de datahoras, menos o fuso horário. @Override public HTMLTools initialize(Server server) { this.server = server; this.timestampConverter = new SimpleDateFormat(STANDARD_TIMESTAMP_FORMAT); timestampConverter.setTimeZone(TimeZone.getTimeZone(UTC_TIMEZONE)); return this; } @Override public String formatTimestamp(long timestamp, String timezone) { // Por enquanto, o fuso horário deve ser UTC: assert timezone.equals(UTC_TIMEZONE); return this.timestampConverter.format(new Date(timestamp * 1000)) + " " + timezone; } @Override public long parseTimestamp(String timestamp) { // Exige que o fuso horário seja UTC por enquanto. String timezone = UTC_TIMEZONE; int tzk = timestamp.indexOf(timezone); assert tzk >= 0; timestamp = timestamp.substring(0,tzk).trim(); try { Date date = this.timestampConverter.parse(timestamp); return date.getTime() / 1000; } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); return -1L; } } @Override public void writePageHeader(PrintWriter wr, User requestor, String title) throws IOException { wr.format("\n"); wr.format("\n"); wr.format("\n"); wr.format("\n"); wr.format("%s\n", title); writeStyles(wr); wr.format("\n"); wr.format("\n"); writeNavbar(wr, requestor); } @Override public void writePageTrailer(PrintWriter wr, User requestor) throws IOException { wr.format("\n"); wr.format("\n"); } @Override public void writeListWithNavLinks ( PrintWriter wr, User requestor, List list, int start, int n, String requestCmd, User user, int maxN ) throws IOException { if (list != null) { int iniIndex = start; int finIndex = start + n - 1; writeMoreItemsReqLink(wr, "Previous", requestCmd, user, iniIndex, -1, maxN); Object first = list.get(start); if (first.getClass() == MessageImpl.class) { // Lista de mensagens: writeMessageList(wr, requestor, (List)list, start, n); } else if (first.getClass() == UserImpl.class) { // Lista de usuários: writeUserList(wr, requestor, (List)list, start, n); } else if (first.getClass() == ContactImpl.class) { // Lista de contatos; decide se são diretos ou reversos: boolean targets; if (((Contact)first).getTarget() == user) { targets = false; } else if (((Contact)first).getSource() == user) { targets = true; } else { assert(false); targets = true; // Para o compilador não reclamar. } writeContactList(wr, requestor, (List)list, targets, start, n); } else { assert(false); } writeMoreItemsReqLink(wr, "Next", requestCmd, user, finIndex, +1, maxN); } else { // !!{ Deveria escrever algo mais amigável do que uma página vazia: }!! } } @Override public void writeMessageList(PrintWriter wr, User requestor, List list, int start, int n) throws IOException { if (list != null) { boolean full = false; // Mensagens numa lista são mostradas de forma abreviada. for (int i = start; i < start + n; i++) { Message message = list.get(i); this.writeMessage(wr, requestor, message, full); } } } @Override public void writeUserList(PrintWriter wr, User requestor, List list, int start, int n) throws IOException { if (list != null) { for (int i = start; i < start + n; i++) { User user = list.get(i); Contact contact = requestor.getDirectContact(user); this.writeUser(wr, requestor, user, contact); } } } @Override public void writeContactList(PrintWriter wr, User requestor, List list, boolean targets, int start, int n) throws IOException { if (list != null) { for (int i = start; i < start + n; i++) { Contact contact = list.get(i); User source = contact.getSource(); User target = contact.getTarget(); assert((requestor == null) || (source == requestor)); User user = (targets ? target : source); this.writeUser(wr, requestor, user, contact); } } } @Override public void writeMessage(PrintWriter wr, User requestor, Message message, boolean full) throws IOException { User poster = message.getPoster(); // Get original message and author: Message original = message; while (original.getParent() != null) { original = original.getParent(); } User author = original.getPoster(); wr.format("\n"); // Author line: wr.format("\n"); wr.format(" \n"); this.writeMessageUserFields(wr, "written by", author, original.getPostedTime()); wr.format("\n"); // Message text box: wr.format("\n"); wr.format(" \n"); wr.format("\n"); if (original != message) { // Reposting line: wr.format("\n"); this.writeMessageUserFields(wr, "reposted by", poster, message.getPostedTime()); wr.format("\n"); } // Buttons line: // !!{ Gerar botões etc. }!! wr.format("\n"); wr.format(" \n"); wr.format("\n"); wr.format("
"); writeUserPhoto(wr, author.getLoginName()); wr.format("
"); this.writeShowMessageReqLink(wr, original.getBody(), author, original.getPostedTime()); wr.format("
[Buttons go here]
\n"); } private void writeMessageUserFields(PrintWriter wr, String label, User user, long timestamp) throws IOException // Writes the login name and full name of {user} and the date {timestamp} // as two fields of the table generated by {writeMessage}. This // method is used both for the author and for reposters of a message. { // User field: String loginName = user.getLoginName(); // !!{ Must sanitize the users's full name to prevent HTML insertion }!! String fullName = user.getFullName(); wr.format(" %s\n", label); wr.format(" "); this.writeShowUserReqLink(wr, loginName, user); wr.println(); wr.format(" %s", fullName); wr.format(" \n"); // Date field: String date = formatTimestamp(timestamp, "UTC"); wr.format(" %s\n", date); } @Override public void writeUser(PrintWriter wr, User requestor, User user, Contact contact) throws IOException { // !!{ Gerar botões etc. }!! String loginName = user.getLoginName(); String fullName = user.getFullName(); String timestamp = formatTimestamp(user.getCreationTime(), "UTC"); // !!{ Use the contact. }!! wr.format("\n"); wr.format("\n"); wr.format(" \n"); wr.format(" \n"); wr.format(" \n", timestamp); wr.format("\n"); wr.format("
"); writeUserPhoto(wr, loginName); wr.format("\n"); wr.format(" "); writeShowUserReqLink(wr, "@" + user.getLoginName(), user); wr.println(); wr.format(" %s", fullName); wr.format(" %s
\n"); } @Override public void writeMoreItemsReqLink( PrintWriter wr, String label, String requestCmd, User user, int start, int dir, long maxN ) throws IOException { wr.format("", maxN); wr.format("%s", label); wr.format(""); } public void writeUserPhoto(PrintWriter wr, String loginName) { wr.format("", loginName); } public void writeReqLink(PrintWriter wr, String style, String label, String requestCmd, User user) // Escreve em {wr} um link para a pseudo-página {requestCmd}. Se {user} não for {null}, // acrescenta ao URL um "?" e o parâmetro "user={user.getLoginName()}". // A aparência do link será a cadeia {label} comestilo {style}. { wr.format(""); wr.format("%s", style, label); wr.format(""); } public void writeShowUserReqLink(PrintWriter wr, String label, User user) // Escreve em {wr} um link para mostrar o perfil do usuário {user}. { writeReqLink(wr, "userLink", label, "ShowUserProfile", user); } public void writeShowMessageReqLink(PrintWriter wr, String text, User author, long timestamp) { // !!{ Must sanitize the message text to prevent HTML insertion }!! // !!{ Must turn embedded "@{loginName}" into active links }!! wr.format("", author.getLoginName(), timestamp); wr.format("%s", text); wr.format(""); } static String NAVBAR_BG_COLOR = "rgba(255, 255, 255, 0.75)"; static String NAVBAR_BODY_BG_COLOR = "#888"; static String NAVBAR_BOX_BG_COLOR = "#FFF"; static String LOGIN_COLOR = "#22F"; static String LINK_COLOR = "#22F"; static String DATETIME_COLOR = "#808080"; static String TEXT_FONTS = "'Helvetica Neue','Arial',sans-serif"; private void writeStyles(PrintWriter wr) throws IOException { wr.format("\n"); } private void writeNavbar(PrintWriter wr, User requestor) throws IOException { // Escreve em {wr} o HTML da barra de navegação Quack. // // Se {requstor} não for {null}, supõe que é o dono da sessão. A barra terá // botões para listar perfil e timeline, e logout. // // Se {requestor} for {null}, não tem esses botões, e tem botão para fazer login. wr.format("
\n"); wr.format(" \n"); wr.format("
\n"); } private void writeMessageEditForm(PrintWriter wr) throws IOException { wr.format("
\n"); wr.format(" \n"); wr.format("

\n"); wr.format("
\n"); } }