com.turt2live.xmail.mail.Mail.java Source code

Java tutorial

Introduction

Here is the source code for com.turt2live.xmail.mail.Mail.java

Source

/*******************************************************************************
 * 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);
    }

}