Java tutorial
/******************************************************************************* * Copyright (C) 2014 Travis Ralston (turt2live) * * 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 com.turt2live.xmail.mail; import com.turt2live.xmail.XMail; import com.turt2live.xmail.XMailConfig; import com.turt2live.xmail.XMailMessage; import com.turt2live.xmail.api.Attachments; import com.turt2live.xmail.entity.XMailEntity; import com.turt2live.xmail.impl.bukkit.internalapi.pages.Line; import com.turt2live.xmail.mail.attachment.*; import com.turt2live.xmail.mail.attachment.Attachment.AttachmentType; import com.turt2live.xmail.utils.ArrayArrayList; import org.bukkit.ChatColor; import org.bukkit.inventory.ItemStack; import org.json.simple.JSONValue; import org.json.simple.parser.ContainerFactory; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import java.io.Serializable; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.*; import java.util.Map.Entry; /** * Represents a mail object * * @author turt2live */ public class Mail extends Line implements Cloneable, Serializable { private static final long serialVersionUID = 5172483903725677317L; private int uuid = 0; protected List<Attachment> attachments; protected int localId; protected boolean read, viewOnly = false; protected List<String> tags; protected long timestamp; protected String to, from, sentFrom; /** * Creates a new mail object with the following defaults:<br> * <ul> * <li>Unread</li> * <li>Sent "now"</li> * <li>This server's IP</li> * <li>No custom tags</li> * </ul> * * @param to who the mail is from * @param from who the mail is from * @param attachments the attachments to add */ public Mail(String to, String from, Attachment... attachments) { if (to == null || from == null) { throw new IllegalArgumentException(); } read = false; sentFrom = "localhost"; timestamp = System.currentTimeMillis() / 1000l; this.to = to; this.from = from; this.attachments = new ArrayArrayList<Attachment>(attachments); recalculateTags(); recalculateAttachments(); } /** * Creates a new mail object with the following defaults:<br> * <ul> * <li>Unread</li> * <li>Sent "now"</li> * <li>This server's IP</li> * <li>No custom tags</li> * </ul> * * @param to who the mail is from * @param from who the mail is from * @param attachments the attachments to add */ public Mail(String to, String from, List<Attachment> attachments) { if (to == null || from == null) { throw new IllegalArgumentException(); } read = false; sentFrom = "localhost"; timestamp = System.currentTimeMillis() / 1000l; this.to = to; this.from = from; this.attachments = new ArrayArrayList<Attachment>(attachments); recalculateTags(); recalculateAttachments(); } /** * Adds an attachment to this mail message * * @param attachment the attachment to add, cannot be null */ public void addAttachment(Attachment attachment) { if (attachment == null) { throw new IllegalArgumentException(); } this.attachments.add(attachment); recalculateAttachments(); } /** * Adds a tag to the message * * @param tag the tag to add, cannot be null */ public void addTag(String tag) { if (tag == null) { throw new IllegalArgumentException(); } this.tags.add(tag); recalculateTags(); } /** * Clones this mail message */ @Override public Mail clone() { Mail m = new Mail(getTo(), getFrom(), getAttachments()); m.setTimestamp(getTimestamp()); m.setServerSender(getServerSender()); for (String tag : getTags()) { m.addTag(tag); } return m; } /** * Clones this mail message with a specified "to" field * * @param to the name of the 'to' field. * * @return the customized clone or {@link #clone()} if "to" is null */ public Mail clone(String to) { if (to == null) { return clone(); } Mail m = new Mail(to, getFrom(), getAttachments()); m.setTimestamp(getTimestamp()); m.setServerSender(getServerSender()); for (String tag : getTags()) { m.addTag(tag); } return m; } /** * Clones this mail message with a specified "to" and "from" field * * @param to the name of the 'to' field. * @param from the name of the 'from' field * * @return the customized clone or {@link #clone()} if "to" or "from" is null */ public Mail clone(String to, String from) { if (to == null || from == null) { return clone(); } Mail m = new Mail(to, from, getAttachments()); m.setTimestamp(getTimestamp()); m.setServerSender(getServerSender()); for (String tag : getTags()) { m.addTag(tag); } return m; } /** * Determines if the mail message does NOT contain specific attachment types * * @param types the types to check * * @return true if the attachment types were NOT found */ public boolean doesNotHaveAttachmentType(AttachmentType... types) { if (types == null || types.length == 0) { return false; } List<AttachmentType> typeLs = new ArrayArrayList<AttachmentType>(types); for (Attachment att : attachments) { if (typeLs.contains(att.getType())) { return false; } } return true; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof Mail)) { return false; } Mail other = (Mail) obj; if (attachments == null) { if (other.attachments != null) { return false; } } else if (!attachments.equals(other.attachments)) { return false; } if (from == null) { if (other.from != null) { return false; } } else if (!from.equals(other.from)) { return false; } if (localId != other.localId) { return false; } if (read != other.read) { return false; } if (sentFrom == null) { if (other.sentFrom != null) { return false; } } else if (!sentFrom.equals(other.sentFrom)) { return false; } if (tags == null) { if (other.tags != null) { return false; } } else if (!tags.equals(other.tags)) { return false; } if (timestamp != other.timestamp) { return false; } if (to == null) { if (other.to != null) { return false; } } else if (!to.equals(other.to)) { return false; } if (uuid != other.uuid) { return false; } if (viewOnly != other.viewOnly) { return false; } return true; } @Override public void format() { String message = getMessage(); super.setLine(ChatColor.GOLD + "#" + getLocalID() + " " + ChatColor.DARK_AQUA + getFrom() + ChatColor.AQUA + ": " + ChatColor.BLUE + message.substring(0, message.length() > 25 ? 25 : message.length())); } /** * Determines if this mail is from this server. This only verifies if the mail is from a Minecraft server (this one specifically). * If the mail is from a website, desktop application, or otherwise not a Minecraft server then this will return true as * * @return true if this mail is to be considered from this server, false otherwise */ public boolean fromThisServer() { String ip = getServerSender(); if (ip == null) { return true; } try { InetAddress add = InetAddress.getByName(ip); if (add.isAnyLocalAddress() || add.isLoopbackAddress()) { return true; } } catch (UnknownHostException e) { e.printStackTrace(); } return XMailConfig.getIP().equalsIgnoreCase(ip); } /** * Gets an unmodifiable list of the attachments on this message * * @return the list of attachments */ public List<Attachment> getAttachments() { return Collections.unmodifiableList(attachments); } /** * Gets the signature of all the attachments, such as "13 items, $50.00" * * @return the attachment signature */ public String getAttachmentSignature() { return Attachment.getSignature(this); } /** * Gets the amount of COD applied to this message * * @return the amount of COD */ public double getCODAmount() { double amount = 0; for (Attachment attachment : getAttachments()) { if (attachment.getType() == Attachment.AttachmentType.MONEY) { amount += ((CODAttachment) attachment).getAmount(); } } return amount; } /** * Gets who the mail was sent from * * @return the sender of this mail message */ public String getFrom() { return from; } /** * Gets a list of items applied to this message * * @return the list of items. Never null, may be empty */ public List<ItemStack> getItems() { List<ItemStack> items = new ArrayList<ItemStack>(); for (Attachment attachment : getAttachments()) { if (attachment.getType() == Attachment.AttachmentType.ITEM) { items.add(((ItemAttachment) attachment).getItemStack()); } } return items; } /** * Gets the local ID of this mail object * * @return the local ID of this mail object */ public int getLocalID() { return localId; } /** * Attempts to find a message in the mail object. This will look for the first message attachment and return it's value. * * @return the message of the mail. If no message is within the mail then "No Message" will be returned */ public String getMessage() { String message = "No Message"; for (Attachment attachment : getAttachments()) { if (attachment.getType() == Attachment.AttachmentType.MESSAGE) { if (attachment.getContent() instanceof String) { message = (String) attachment.getContent(); break; } } } return message; } /** * Gets the amount of money applied to this message * * @return the amount of money */ public double getMoneyAmount() { double amount = 0; for (Attachment attachment : getAttachments()) { if (attachment.getType() == Attachment.AttachmentType.MONEY) { amount += ((MoneyAttachment) attachment).getAmount(); } } return amount; } /** * Gets the IP (or location) of the source of this message * * @return the IP (or location) of this message's sender */ public String getServerSender() { return sentFrom; } /** * Gets an unmodifiable list of the tags on this message * * @return the list of tags */ public List<String> getTags() { return Collections.unmodifiableList(tags); } /** * Gets the timestamp of this message * * @return the timestamp of this message */ public long getTimestamp() { return timestamp; } /** * Gets who this mail was sent to * * @return who the mail is to */ public String getTo() { return to; } /** * Gets the UUID of this message * * @return the UUID of this message */ public int getUUID() { return uuid; } /** * Distributes and handles all attachments on this mail message. If the entity is not capable of holding an attachment (such as the console * and an item) then the attachment(s) that are not supported are sent to the entity in a new message. For example if the console gets a stack * of dirt in the mail, the stack of dirt will instead be sent to them via a new message. * * @param entity the XMailEntity to deliver the attachments to. */ public void giveAttachments(XMailEntity entity) { List<Attachment> failedAttachments = new ArrayList<Attachment>(); for (Attachment attachment : getAttachments()) { boolean given = attachment.giveTo(entity); if (given) { attachment.onGive(entity, this); } else { failedAttachments.add(attachment); } } if (failedAttachments.size() > 0) { failedAttachments.add(new MessageAttachment( "Missing attachments - We were unable to give you the attachments included in this message.")); Mail out = new Mail(entity.getName(), XMail.getConsole().getName(), failedAttachments); XMail.getInstance().getMailServer().broadcastSend(out); XMailMessage.sendMessage(entity.getOwner(), ChatColor.ITALIC + "" + ChatColor.WHITE + "Not all attachments were able to be given to you. They have been re-sent to you.", true); } XMailMessage.sendMessage(entity.getOwner(), ChatColor.GREEN + Attachment.getReceiveText(this, failedAttachments), false); } /** * Determines if the mail message contains specific attachment types * * @param types the types to check * * @return true if the attachment types were found */ public boolean hasAttachmentType(AttachmentType... types) { if (types == null || types.length == 0) { return false; } List<AttachmentType> typeLs = new ArrayArrayList<AttachmentType>(types); for (Attachment att : attachments) { if (typeLs.contains(att.getType())) { return true; } } return false; } /** * Determines if this message is read * * @return true if read */ public boolean isRead() { return this.read; } /** * Determines if this message is to be treated as "view only" * * @return true if view only mode is active */ public boolean isViewOnly() { return viewOnly; } /** * Marks the mail message as read. Does not update the server */ public void markRead() { this.read = true; recalculateTags(); } /** * Marks the mail message as unread. Does not update the server */ public void markUnread() { this.read = false; recalculateTags(); } /** * Removes an attachment from this mail message * * @param attachment the attachment to remove, cannot be null */ public void removeAttachment(Attachment attachment) { if (attachment == null) { throw new IllegalArgumentException(); } this.attachments.remove(attachment); recalculateAttachments(); } /** * Removes a tag from this message * * @param tag the tag to remove, cannot be null */ public void removeTag(String tag) { if (tag == null) { throw new IllegalArgumentException(); } this.tags.remove(tag); recalculateTags(); } /** * Sets the local ID of this mail. This is for internal use and does not affect anythign server-side * * @param i the new local ID */ public void setLocalID(int i) { this.localId = i; } /** * Sets where this mail was sent from * * @param ip the IP where this mail was sent from, cannot be null. */ public void setServerSender(String ip) { if (ip == null) { throw new IllegalArgumentException(); } this.sentFrom = ip; } /** * Sets the timestamp of this message. * * @param timestamp the timestamp of this message in seconds since the Unix epoch */ public void setTimestamp(long timestamp) { this.timestamp = timestamp; } /** * Sets the view only flag of this message * * @param viewOnly the view only flag */ public void setViewOnly(boolean viewOnly) { this.viewOnly = viewOnly; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Mail [getUUID()=").append(getUUID()).append(", getAttachmentSignature()=") .append(getAttachmentSignature()).append(", getCODAmount()=").append(getCODAmount()) .append(", getMoneyAmount()=").append(getMoneyAmount()).append(", getItems()=").append(getItems()) .append(", getMessage()=").append(getMessage()).append(", getLocalID()=").append(getLocalID()) .append(", isRead()=").append(isRead()).append(", getFrom()=").append(getFrom()) .append(", getTo()=").append(getTo()).append(", getServerSender()=").append(getServerSender()) .append(", getTimestamp()=").append(getTimestamp()).append(", getAttachments()=") .append(getAttachments()).append(", getTags()=").append(getTags()).append(", isViewOnly()=") .append(isViewOnly()).append(", fromThisServer()=").append(fromThisServer()).append("]"); return builder.toString(); } protected final void recalculateAttachments() { double moneyBalance = 0; List<Attachment> keep = new ArrayList<Attachment>(); for (Attachment a : attachments) { switch (a.getType()) { case MONEY: moneyBalance += ((MoneyAttachment) a).getAmount(); break; case COD: moneyBalance -= ((CODAttachment) a).getAmount(); break; // TODO: Item attachment compression default: keep.add(a); } } double abs = Math.abs(moneyBalance); if (moneyBalance > 0) { keep.add(new MoneyAttachment(abs)); } else if (moneyBalance < 0) { keep.add(new CODAttachment(abs)); } // else moneyBalance == 0 then do not add an attachment this.attachments = keep; } protected final void recalculateTags() { List<String> newTags = new ArrayList<String>(); newTags.add(isRead() ? "read" : "unread"); if (this.tags != null) { for (String tag : tags) { if (!newTags.contains(tag.toLowerCase()) && !(tag.equalsIgnoreCase("read") || tag.equalsIgnoreCase("unread"))) { newTags.add(tag.toLowerCase()); } } } this.tags = newTags; } final void setUUID(int uuid) { this.uuid = uuid; } /** * Creates a new mail object from a JSON string * * @param map the json string * * @return the NewMail object or null if the json is invalid */ public static Mail fromJSON(Map<String, Object> map) { boolean debug = true; // Internal debug flag // Startup variables String to, from, sentFrom; boolean unread; long time; List<String> tags; int uuid = 0; // Do null and instance checks on EVERYTHING we need Object o = map.get("to"); if (o == null || !(o instanceof String)) { if (debug) { System.out.println("[xMail] Err 1"); } return null; } to = (String) o; o = map.get("from"); if (o == null || !(o instanceof String)) { if (debug) { System.out.println("[xMail] Err 2"); } return null; } from = (String) o; o = map.get("sent-from"); if (o == null || !(o instanceof String)) { if (debug) { System.out.println("[xMail] Err 3"); } return null; } sentFrom = (String) o; o = map.get("date"); if (o == null) { // Gson casts Long->Double if (debug) { System.out.println("[xMail] Err 4"); } return null; } if (o instanceof String) { try { o = Double.parseDouble((String) o); } catch (NumberFormatException e) { if (debug) { System.out.println("[xMail] Err 4-1"); } return null; } } time = ((Double) o).longValue(); o = map.get("unread"); if (o == null || !(o instanceof Boolean)) { if (debug) { System.out.println("[xMail] Err 5"); } return null; } unread = (Boolean) o; o = map.get("attachments"); if (o == null || !(o instanceof List)) { if (debug) { System.out.println("[xMail] Err 6"); } return null; } List<?> list = (List<?>) o; // We have to convert the list to a list of Strings List<String> list2 = new ArrayList<String>(); List<Attachment> a2 = new ArrayList<Attachment>(); for (Object o2 : list) { // Attachments will not be String lists, they will be <String, Object> maps because of Gson if (o2 instanceof Map) { Map<?, ?> item = (Map<?, ?>) o2; // Safe cast technique String jVal = JSONValue.toJSONString(item); // Put it back in JSON for Attachments class Attachment a = Attachments.getAttachment(jVal); // Generate the attachment if (a != null) { a2.add(a); // Add the attachment to the list } } } o = map.get("tags"); if (o == null || !(o instanceof List)) { if (debug) { System.out.println("[xMail] Err 7"); } return null; } list = (List<?>) o; o = map.get("uuid"); if (o == null) { if (debug) { System.out.println("[xMail] Err 8"); } return null; } else if (o instanceof String) { try { o = Integer.parseInt((String) o); } catch (NumberFormatException e) { if (debug) { System.out.println("[xMail] Err 8-1"); } return null; } } else if (!(o instanceof Integer)) { if (debug) { System.out.println("[xMail] Err 9"); } return null; } uuid = (Integer) o; // We have to convert the list to a list of Strings list2 = new ArrayList<String>(); for (Object o2 : list) { if (o2 instanceof String) { list2.add((String) o2); } } tags = new ArrayList<String>(list2); // Now construct the mail object Mail mail = new Mail(to, from, a2); mail.setServerSender(sentFrom); mail.setTimestamp(time); mail.setUUID(uuid); if (unread) { mail.markUnread(); } else { mail.markRead(); } for (String tag : tags) { mail.addTag(tag); } // Throw it back return mail; } /** * Creates a new mail object from a JSON string * * @param json the json string * * @return the NewMail object or null if the json is invalid */ @SuppressWarnings("unchecked") public static Mail fromJSON(String json) { // Construct map from JSON JSONParser parser = new JSONParser(); ContainerFactory containerFactory = new ContainerFactory() { @Override public List<Object> creatArrayContainer() { return new LinkedList<Object>(); } @Override public Map<String, Object> createObjectContainer() { return new LinkedHashMap<String, Object>(); } }; Map<String, Object> map = new HashMap<String, Object>(); try { Map<?, ?> jsonMap = (Map<?, ?>) parser.parse(json, containerFactory); Iterator<?> iter = jsonMap.entrySet().iterator(); // Type check while (iter.hasNext()) { Entry<?, ?> entry = (Entry<?, ?>) iter.next(); if (!(entry.getKey() instanceof String)) { throw new IllegalArgumentException("Not in <String, Object> format"); } } map = (Map<String, Object>) jsonMap; } catch (ParseException e) { e.printStackTrace(); return null; } catch (IllegalArgumentException e) { e.printStackTrace(); return null; } return fromJSON(map); } }