com.techcavern.pircbotz.Channel.java Source code

Java tutorial

Introduction

Here is the source code for com.techcavern.pircbotz.Channel.java

Source

/**
 * Copyright (C) 2014 Julian Zhou <jzhou at techcavern.com>
 *
 * This file is part of PircBotZ.
 *
 * PircBotZ 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.
 *
 * PircBotZ 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 PircBotZ. If not, see <http://www.gnu.org/licenses/>.
 */
package com.techcavern.pircbotz;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterators;
import com.google.common.collect.PeekingIterator;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.concurrent.AtomicSafeInitializer;
import org.apache.commons.lang3.concurrent.ConcurrentException;
import com.techcavern.pircbotz.hooks.managers.ThreadedListenerManager;
import com.techcavern.pircbotz.output.OutputChannel;
import com.techcavern.pircbotz.snapshot.ChannelSnapshot;

/**
 * Represents a Channel that we're joined to. 
 * @author Originally by:
 * <a href="http://pircbotx.googlecode.com">Leon Blakey <lord.quackstar at gmail.com> in PircBotX</a>
 * <p>Forked and Maintained by Julian Zhou <jzhou at techcavern.com> in <a href="https://github.com/TechCavern/PircBotZ">PircBotZ</a> */
@ToString(doNotUseGetters = true)
@EqualsAndHashCode(of = { "name", "bot" })
@Slf4j
@Getter
@Setter(AccessLevel.PROTECTED)
public class Channel implements Comparable<Channel> {
    /**
     * The name of the channel. Will never change
     */
    protected final String name;
    /**
     * Unique UUID for this channel <i>instance</i>
     */
    protected final UUID channelId = UUID.randomUUID();
    @Getter(AccessLevel.PROTECTED)
    protected final UserChannelDao<User, Channel> dao;
    /**
     * The bot that this channel came from
     */
    protected final PircBotZ bot;
    //Output is lazily created since it might not ever be used
    @Getter(AccessLevel.NONE)
    protected final AtomicSafeInitializer<OutputChannel> output = new AtomicSafeInitializer<OutputChannel>() {
        @Override
        protected OutputChannel initialize() {
            return bot.getConfiguration().getBotFactory().createOutputChannel(bot, Channel.this);
        }
    };
    @Setter(AccessLevel.NONE)
    protected String mode = "";
    /**
     * The current channel topic
     */
    protected String topic = "";
    /**
     * Timestamp of when the topic was created. Defaults to 0
     */
    protected long topicTimestamp;
    /**
     * Timestamp of when channel was created. Defaults to 0
     */
    protected long createTimestamp;
    /**
     * The user who set the topic. Default is blank
     */
    protected String topicSetter = "";
    /**
     * Moderated (+m) status
     */
    protected boolean moderated = false;
    /**
     * No external messages (+n) status
     */
    protected boolean noExternalMessages = false;
    /**
     * Invite only (+i) status
     */
    protected boolean inviteOnly = false;
    /**
     * Secret (+s) status
     */
    protected boolean secret = false;
    /**
     * Private (+p) status
     */
    protected boolean channelPrivate = false;
    @Getter(AccessLevel.NONE)
    protected boolean topicProtection = false;
    /**
     * Channel limit (+l #)
     */
    protected int channelLimit = -1;
    /**
     * Channel key (+k)
     */
    protected String channelKey = null;
    @Getter(AccessLevel.NONE)
    @Setter(AccessLevel.NONE)
    protected boolean modeStale = false;
    @Getter(AccessLevel.NONE)
    @Setter(AccessLevel.NONE)
    protected CountDownLatch modeLatch = null;

    @SuppressWarnings("unchecked")
    protected Channel(PircBotZ bot, UserChannelDao<? extends User, ? extends Channel> dao, String name) {
        this.bot = bot;
        this.dao = (UserChannelDao<User, Channel>) dao;
        this.name = name;
    }

    /**
     * Send a line to the channel.
     * @return A {@link OutputChannel} for this channel
     */
    public OutputChannel send() {
        try {
            return output.get();
        } catch (ConcurrentException ex) {
            throw new RuntimeException("Could not generate OutputChannel for " + getName(), ex);
        }
    }

    protected void parseMode(String rawMode) {
        if (rawMode.contains(" ")) {
            //Mode contains arguments which are impossible to parse.
            //Could be a ban command (we shouldn't use this), channel key (should, but where), etc
            //Need to ask server
            modeStale = true;
            return;
        }

        //Parse mode by switching between removing and adding by the existance of a + or - sign
        boolean adding = true;
        for (char curChar : rawMode.toCharArray())
            if (curChar == '-')
                adding = false;
            else if (curChar == '+')
                adding = true;
            else if (adding)
                mode = mode + curChar;
            else
                mode = mode.replace(Character.toString(curChar), "");
    }

    /**
     * Gets the channel mode. If mode is simple (no arguments), this will return immediately.
     * If its not (mode with arguments, eg channel key), then asks the server for the
     * correct mode, waiting until it gets a response
     * <p>
     * <b>WARNING:</b> Because of the last checking, a threaded listener manager like
     * {@link ThreadedListenerManager} is required. Using a single threaded listener
     * manager like {@link com.techcavern.pircbotz.hooks.managers.GenericListenerManager} will
     * mean this method <i>never returns</i>!
     * @return A known good mode, either immediately or soon.
     */
    public String getMode() {
        if (!modeStale)
            return mode;

        //Mode is stale, get new mode from server
        try {
            log.debug("Mode is stale for channel " + getName() + ", fetching fresh mode");
            if (modeLatch == null || modeLatch.getCount() == 0)
                modeLatch = new CountDownLatch(1);
            bot.sendRaw().rawLine("MODE " + getName());
            //Wait for setMode to be called
            modeLatch.await();
            //We have known good mode from server, now return
            return mode;
        } catch (InterruptedException e) {
            throw new RuntimeException("Waiting for mode response interrupted", e);
        }
    }

    /**
     * Check if the channel has topic protection (+t) set.
     * @return True if +t   
     */
    public boolean hasTopicProtection() {
        return topicProtection;
    }

    /**
     * Get all levels the user holds in this channel.
     * @param user The user to get the levels of
     * @return An <b>immutable copy</b> of the levels the user holds
     */
    public ImmutableSortedSet<UserLevel> getUserLevels(User user) {
        return getDao().getLevels(this, user);
    }

    /**
     * Get all users that don't have any special status in this channel. This means
     * that they aren't ops, have voice, superops, halops, or owners in this channel
     * @return An <b>immutable copy</b> of normal users
     */
    public ImmutableSortedSet<User> getNormalUsers() {
        return getDao().getNormalUsers(this);
    }

    /**
     * Get all opped users in this channel.
     * @return An <b>immutable copy</b> of opped users
     */
    public ImmutableSortedSet<User> getOps() {
        return getDao().getUsers(this, UserLevel.OP);
    }

    /**
     * Get all voiced users in this channel.
     * @return An <b>immutable copy</b> of voiced users
     */
    public ImmutableSortedSet<User> getVoices() {
        return getDao().getUsers(this, UserLevel.VOICE);
    }

    /**
     * Get all users with Owner status in this channel.
     * @return An <b>immutable copy</b> of users with Owner status
     */
    public ImmutableSortedSet<User> getOwners() {
        return getDao().getUsers(this, UserLevel.OWNER);
    }

    /**
     * Get all users with Half Operator status in this channel.
     * @return An <b>immutable copy</b> of users with Half Operator status
     */
    public ImmutableSortedSet<User> getHalfOps() {
        return getDao().getUsers(this, UserLevel.HALFOP);
    }

    /**
     * Get all users with Super Operator status in this channel.
     * @return An <b>immutable copy</b> of users with Super Operator status
     */
    public ImmutableSortedSet<User> getSuperOps() {
        return getDao().getUsers(this, UserLevel.SUPEROP);
    }

    /**
     * Sets the mode of the channel. If there is a getMode() waiting on this,
     * fire it.
     * @param mode
     */
    protected void setMode(String mode, ImmutableList<String> modeParsed) {
        this.mode = mode;
        this.modeStale = false;
        if (modeLatch != null)
            modeLatch.countDown();

        //Parse out mode
        PeekingIterator<String> params = Iterators.peekingIterator(modeParsed.iterator());

        //Process modes letter by letter, grabbing paramaters as needed
        boolean adding = true;
        String modeLetters = params.next();
        for (int i = 0; i < modeLetters.length(); i++) {
            char curModeChar = modeLetters.charAt(i);
            if (curModeChar == '+')
                adding = true;
            else if (curModeChar == '-')
                adding = false;
            else {
                ChannelModeHandler modeHandler = bot.getConfiguration().getChannelModeHandlers().get(curModeChar);
                if (modeHandler != null)
                    modeHandler.handleMode(bot, this, null, params, adding, false);
            }
        }
    }

    /**
     * Get all users in this channel. Simply calls {@link PircBotZ#getUsers(com.techcavern.pircbotz.Channel) }
     * @return An <i>Unmodifiable</i> Set of users in this channel
     */
    public ImmutableSortedSet<User> getUsers() {
        return getDao().getUsers(this);
    }

    /**
     * Get the user that set the topic. As the user may or may not be in the
     * channel return as a string
     * @return The user that set the topic in String format
     */
    public String getTopicSetter() {
        return topicSetter;
    }

    /**
     * Checks if the given user is an Operator in this channel
     * @return True if the user is an Operator, false if not
     */
    public boolean isOp(User user) {
        return getDao().levelContainsUser(UserLevel.OP, this, user);
    }

    /**
     * Checks if the given user has Voice in this channel.
     * @return True if the user has Voice, false if not
     */
    public boolean hasVoice(User user) {
        return getDao().levelContainsUser(UserLevel.VOICE, this, user);
    }

    /**
     * Checks if the given user is a Super Operator in this channel.
     * @return True if the user is a Super Operator, false if not
     */
    public boolean isSuperOp(User user) {
        return getDao().levelContainsUser(UserLevel.SUPEROP, this, user);
    }

    /**
     * Checks if the given user is an Owner in this channel.
     * @return True if the user is an Owner, false if not
     */
    public boolean isOwner(User user) {
        return getDao().levelContainsUser(UserLevel.OWNER, this, user);
    }

    /**
     * Checks if the given user is a Half Operator in this channel.
     * @return True if the user is a Half Operator, false if not
     */
    public boolean isHalfOp(User user) {
        return getDao().levelContainsUser(UserLevel.HALFOP, this, user);
    }

    /**
     * Create an immutable snapshot of this channel. 
     * @return Immutable Channel copy minus the DAO
     */
    public ChannelSnapshot createSnapshot() {
        if (modeStale)
            log.warn("Channel {} mode '{}' is stale", getName(), mode);
        return new ChannelSnapshot(this, mode);
    }

    /**
     * Compare {@link #getName()} with {@link String#compareToIgnoreCase(java.lang.String) }.
     * This is useful for sorting lists of Channel objects.
     * @param other Other channel to compare to
     * @return the result of calling compareToIgnoreCase on channel names.
     */
    public int compareTo(Channel other) {
        return getName().compareToIgnoreCase(other.getName());
    }
}