<commit_before>/*
 * Copyright (c) 2006-2008 <NAME>, <NAME>, <NAME>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * SVN: $Id$
 */

package com.dmdirc.parser;

import com.dmdirc.parser.callbacks.CallbackOnChannelSingleModeChanged;
import com.dmdirc.parser.callbacks.CallbackOnChannelNonUserModeChanged;
import com.dmdirc.parser.callbacks.CallbackOnChannelModeChanged;
import com.dmdirc.parser.callbacks.CallbackOnChannelUserModeChanged;
import com.dmdirc.parser.callbacks.CallbackOnUserModeChanged;
import com.dmdirc.parser.callbacks.CallbackOnUserModeDiscovered;

import java.util.Calendar;

/**
 * Process a Mode line.
 */
public class ProcessMode extends IRCProcessor {
	/**
	 * Process a Mode Line.
	 *
	 * @param sParam Type of line to process ("MODE", "324")
	 * @param token IRCTokenised line to process
	 */
    @Override
	public void process(String sParam, String[] token) {
		String[] sModestr;
		String sChannelName;
		if (sParam.equals("324")) {
			sChannelName = token[3];
			sModestr = new String[token.length-4];
			System.arraycopy(token, 4, sModestr, 0, token.length-4);
		} else if (sParam.equals("221")) {
			processUserMode(sParam, token, new String[]{token[token.length-1]}, true);
			return;
		} else {
			sChannelName = token[2];
			sModestr = new String[token.length-3];
			System.arraycopy(token, 3, sModestr, 0, token.length-3);
		}

		if (!isValidChannelName(sChannelName)) { processUserMode(sParam, token, sModestr, false); }
		else { processChanMode(sParam, token, sModestr, sChannelName); }
	}
	
	/**
	 * Method to trim spaces from strings
	 *
	 * @param str String to trim
	 * @return String without spaces on the ends
	 */
	private String trim(String str) { return str.trim(); }
	
	/**
	 * Process Chan modes.
	 *
	 * @param sParam String representation of parameter to parse
	 * @param token IRCTokenised Array of the incomming line
	 * @param sModestr The modes and params
	 * @param sChannelName Channel these modes are for
	 */	
	public void processChanMode(String sParam, String token[], String sModestr[], String sChannelName) {
		StringBuilder sFullModeStr = new StringBuilder();
		String sNonUserModeStr = "";
		String sNonUserModeStrParams = "";
		String sModeParam;
		String sTemp;
		int nParam = 1;
		long nTemp = 0, nValue = 0, nCurrent = 0;
		boolean bPositive = true, bBooleanMode = true;
		char cPositive = '+';
		ChannelInfo iChannel;
		ChannelClientInfo iChannelClientInfo;
		ClientInfo iClient;
		ChannelClientInfo setterCCI;
		
		CallbackOnChannelSingleModeChanged cbSingle = null;
		CallbackOnChannelNonUserModeChanged cbNonUser = null;

        if (!sParam.equals("324")) {
			cbSingle = (CallbackOnChannelSingleModeChanged) getCallbackManager().getCallbackType("OnChannelSingleModeChanged");
			cbNonUser = (CallbackOnChannelNonUserModeChanged) getCallbackManager().getCallbackType("OnChannelNonUserModeChanged");
		}
		
		iChannel = getChannelInfo(sChannelName);
		if (iChannel == null) { 
			// callErrorInfo(new ParserError(ParserError.ERROR_WARNING, "Got modes for channel ("+sChannelName+") that I am not on.", myParser.getLastLine()));
			// iChannel = new ChannelInfo(myParser, sChannelName);
			// myParser.addChannel(iChannel);
			return;
		}
		// Get the current channel modes
		if (!sParam.equals("324")) { nCurrent = iChannel.getMode(); }
		
		setterCCI = iChannel.getUser(token[0]);
		if (IRCParser.ALWAYS_UPDATECLIENT && setterCCI != null) {
			// Facilitate dmdirc formatter
			if (setterCCI.getClient().getHost().isEmpty()) {setterCCI.getClient().setUserBits(token[0],false); }
		}
		
		// Loop through the mode string, and add/remove modes/params where they are needed
		for (int i = 0; i < sModestr[0].length(); ++i) {
			Character cMode = sModestr[0].charAt(i);
			if (cMode.equals(":".charAt(0))) { continue; }
			
			sNonUserModeStr = sNonUserModeStr+cMode;
			if (cMode.equals("+".charAt(0))) { cPositive = '+'; bPositive = true; }
			else if (cMode.equals("-".charAt(0))) { cPositive = '-'; bPositive = false; }
			else {
				if (myParser.hChanModesBool.containsKey(cMode)) { nValue = myParser.hChanModesBool.get(cMode); bBooleanMode = true; }
				else if (myParser.hChanModesOther.containsKey(cMode)) { nValue = myParser.hChanModesOther.get(cMode); bBooleanMode = false; }
				else if (myParser.hPrefixModes.containsKey(cMode)) { 
					// (de) OP/Voice someone
					sModeParam = sModestr[nParam++];
					nValue = myParser.hPrefixModes.get(cMode);
					callDebugInfo(IRCParser.DEBUG_INFO, "User Mode: %c / %d [%s] {Positive: %b}",cMode, nValue, sModeParam, bPositive);
					iChannelClientInfo = iChannel.getUser(sModeParam);
					if (iChannelClientInfo == null) {
						// Client not known?
//						callErrorInfo(new ParserError(ParserError.ERROR_WARNING, "Got mode for client not known on channel - Added", myParser.getLastLine()));
						iClient = getClientInfo(sModeParam);
						if (iClient == null) { 
//							callErrorInfo(new ParserError(ParserError.ERROR_WARNING, "Got mode for client not known at all - Added", myParser.getLastLine()));
							iClient = new ClientInfo(myParser, sModeParam);
							myParser.addClient(iClient);
						}
						iChannelClientInfo = iChannel.addClient(iClient);
					}
					callDebugInfo(IRCParser.DEBUG_INFO, "\tOld Mode Value: %d",iChannelClientInfo.getChanMode());
					if (bPositive) { iChannelClientInfo.setChanMode(iChannelClientInfo.getChanMode() | nValue); sTemp = "+"; }
					else { iChannelClientInfo.setChanMode(iChannelClientInfo.getChanMode() ^ (iChannelClientInfo.getChanMode() & nValue)); sTemp = "-"; }
					sTemp = sTemp+cMode;
					callChannelUserModeChanged(iChannel, iChannelClientInfo, setterCCI, token[0], sTemp);
					continue;
				} else {
					// unknown mode - add as boolean
//					callErrorInfo(new ParserError(ParserError.ERROR_WARNING, "Got unknown mode "+cMode+" - Added as boolean mode", myParser.getLastLine()));
					myParser.hChanModesBool.put(cMode,myParser.nNextKeyCMBool);
					nValue = myParser.nNextKeyCMBool;
					bBooleanMode = true;
					myParser.nNextKeyCMBool = myParser.nNextKeyCMBool*2;
				}
				
				if (bBooleanMode) {
					callDebugInfo(IRCParser.DEBUG_INFO, "Boolean Mode: %c [%d] {Positive: %b}",cMode, nValue, bPositive);
					
					if (bPositive) { nCurrent = nCurrent | nValue; }
					else { nCurrent = nCurrent ^ (nCurrent & nValue); }
				} else {
					
					if (bPositive || nValue == IRCParser.MODE_LIST || ((nValue & IRCParser.MODE_UNSET) == IRCParser.MODE_UNSET)) {
						if (sModestr.length <= nParam+1) {
							myParser.callErrorInfo(new ParserError(ParserError.ERROR_FATAL, "Broken Modes. Parameter required but not given.", myParser.getLastLine()));
						}
					}
					
					
					if (nValue == IRCParser.MODE_LIST) {
						// List Mode
						sModeParam = sModestr[nParam++];
						sNonUserModeStrParams = sNonUserModeStrParams+" "+sModeParam;
						nTemp = (Calendar.getInstance().getTimeInMillis() / 1000);
						iChannel.setListModeParam(cMode, new ChannelListModeItem(sModeParam, token[0], nTemp ), bPositive);
						callDebugInfo(IRCParser.DEBUG_INFO, "List Mode: %c [%s] {Positive: %b}",cMode, sModeParam, bPositive);
						if (cbSingle != null) { cbSingle.call(iChannel, setterCCI, token[0], cPositive+cMode+" "+sModeParam ); }
					} else {
						// Mode with a parameter
						if (bPositive) { 
							// +Mode - always needs a parameter to set
							sModeParam = sModestr[nParam++];
							sNonUserModeStrParams = sNonUserModeStrParams+" "+sModeParam;
							callDebugInfo(IRCParser.DEBUG_INFO, "Set Mode: %c [%s] {Positive: %b}",cMode, sModeParam, bPositive);
							iChannel.setModeParam(cMode,sModeParam);
							if (cbSingle != null) { cbSingle.call(iChannel, setterCCI, token[0], cPositive+cMode+" "+sModeParam ); }
						} else {
							// -Mode - parameter isn't always needed, we need to check
							if ((nValue & IRCParser.MODE_UNSET) == IRCParser.MODE_UNSET) {<commit_msg>Fix mode processing. Fixes Issue 1176<commit_after>/*
 * Copyright (c) 2006-2008 <NAME>, <NAME>, <NAME>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * SVN: $Id$
 */

package com.dmdirc.parser;

import com.dmdirc.parser.callbacks.CallbackOnChannelSingleModeChanged;
import com.dmdirc.parser.callbacks.CallbackOnChannelNonUserModeChanged;
import com.dmdirc.parser.callbacks.CallbackOnChannelModeChanged;
import com.dmdirc.parser.callbacks.CallbackOnChannelUserModeChanged;
import com.dmdirc.parser.callbacks.CallbackOnUserModeChanged;
import com.dmdirc.parser.callbacks.CallbackOnUserModeDiscovered;

import java.util.Calendar;

/**
 * Process a Mode line.
 */
public class ProcessMode extends IRCProcessor {
	/**
	 * Process a Mode Line.
	 *
	 * @param sParam Type of line to process ("MODE", "324")
	 * @param token IRCTokenised line to process
	 */
    @Override
	public void process(String sParam, String[] token) {
		String[] sModestr;
		String sChannelName;
		if (sParam.equals("324")) {
			sChannelName = token[3];
			sModestr = new String[token.length-4];
			System.arraycopy(token, 4, sModestr, 0, token.length-4);
		} else if (sParam.equals("221")) {
			processUserMode(sParam, token, new String[]{token[token.length-1]}, true);
			return;
		} else {
			sChannelName = token[2];
			sModestr = new String[token.length-3];
			System.arraycopy(token, 3, sModestr, 0, token.length-3);
		}

		if (!isValidChannelName(sChannelName)) { processUserMode(sParam, token, sModestr, false); }
		else { processChanMode(sParam, token, sModestr, sChannelName); }
	}
	
	/**
	 * Method to trim spaces from strings
	 *
	 * @param str String to trim
	 * @return String without spaces on the ends
	 */
	private String trim(String str) { return str.trim(); }
	
	/**
	 * Process Chan modes.
	 *
	 * @param sParam String representation of parameter to parse
	 * @param token IRCTokenised Array of the incomming line
	 * @param sModestr The modes and params
	 * @param sChannelName Channel these modes are for
	 */	
	public void processChanMode(String sParam, String token[], String sModestr[], String sChannelName) {
		StringBuilder sFullModeStr = new StringBuilder();
		String sNonUserModeStr = "";
		String sNonUserModeStrParams = "";
		String sModeParam;
		String sTemp;
		int nParam = 1;
		long nTemp = 0, nValue = 0, nCurrent = 0;
		boolean bPositive = true, bBooleanMode = true;
		char cPositive = '+';
		ChannelInfo iChannel;
		ChannelClientInfo iChannelClientInfo;
		ClientInfo iClient;
		ChannelClientInfo setterCCI;
		
		CallbackOnChannelSingleModeChanged cbSingle = null;
		CallbackOnChannelNonUserModeChanged cbNonUser = null;

        if (!sParam.equals("324")) {
			cbSingle = (CallbackOnChannelSingleModeChanged) getCallbackManager().getCallbackType("OnChannelSingleModeChanged");
			cbNonUser = (CallbackOnChannelNonUserModeChanged) getCallbackManager().getCallbackType("OnChannelNonUserModeChanged");
		}
		
		iChannel = getChannelInfo(sChannelName);
		if (iChannel == null) { 
			// callErrorInfo(new ParserError(ParserError.ERROR_WARNING, "Got modes for channel ("+sChannelName+") that I am not on.", myParser.getLastLine()));
			// iChannel = new ChannelInfo(myParser, sChannelName);
			// myParser.addChannel(iChannel);
			return;
		}
		// Get the current channel modes
		if (!sParam.equals("324")) { nCurrent = iChannel.getMode(); }
		
		setterCCI = iChannel.getUser(token[0]);
		if (IRCParser.ALWAYS_UPDATECLIENT && setterCCI != null) {
			// Facilitate dmdirc formatter
			if (setterCCI.getClient().getHost().isEmpty()) {setterCCI.getClient().setUserBits(token[0],false); }
		}
		
		// Loop through the mode string, and add/remove modes/params where they are needed
		for (int i = 0; i < sModestr[0].length(); ++i) {
			Character cMode = sModestr[0].charAt(i);
			if (cMode.equals(":".charAt(0))) { continue; }
			
			sNonUserModeStr = sNonUserModeStr+cMode;
			if (cMode.equals("+".charAt(0))) { cPositive = '+'; bPositive = true; }
			else if (cMode.equals("-".charAt(0))) { cPositive = '-'; bPositive = false; }
			else {
				if (myParser.hChanModesBool.containsKey(cMode)) { nValue = myParser.hChanModesBool.get(cMode); bBooleanMode = true; }
				else if (myParser.hChanModesOther.containsKey(cMode)) { nValue = myParser.hChanModesOther.get(cMode); bBooleanMode = false; }
				else if (myParser.hPrefixModes.containsKey(cMode)) { 
					// (de) OP/Voice someone
					sModeParam = sModestr[nParam++];
					nValue = myParser.hPrefixModes.get(cMode);
					callDebugInfo(IRCParser.DEBUG_INFO, "User Mode: %c / %d [%s] {Positive: %b}",cMode, nValue, sModeParam, bPositive);
					iChannelClientInfo = iChannel.getUser(sModeParam);
					if (iChannelClientInfo == null) {
						// Client not known?
//						callErrorInfo(new ParserError(ParserError.ERROR_WARNING, "Got mode for client not known on channel - Added", myParser.getLastLine()));
						iClient = getClientInfo(sModeParam);
						if (iClient == null) { 
//							callErrorInfo(new ParserError(ParserError.ERROR_WARNING, "Got mode for client not known at all - Added", myParser.getLastLine()));
							iClient = new ClientInfo(myParser, sModeParam);
							myParser.addClient(iClient);
						}
						iChannelClientInfo = iChannel.addClient(iClient);
					}
					callDebugInfo(IRCParser.DEBUG_INFO, "\tOld Mode Value: %d",iChannelClientInfo.getChanMode());
					if (bPositive) { iChannelClientInfo.setChanMode(iChannelClientInfo.getChanMode() | nValue); sTemp = "+"; }
					else { iChannelClientInfo.setChanMode(iChannelClientInfo.getChanMode() ^ (iChannelClientInfo.getChanMode() & nValue)); sTemp = "-"; }
					sTemp = sTemp+cMode;
					callChannelUserModeChanged(iChannel, iChannelClientInfo, setterCCI, token[0], sTemp);
					continue;
				} else {
					// unknown mode - add as boolean
//					callErrorInfo(new ParserError(ParserError.ERROR_WARNING, "Got unknown mode "+cMode+" - Added as boolean mode", myParser.getLastLine()));
					myParser.hChanModesBool.put(cMode,myParser.nNextKeyCMBool);
					nValue = myParser.nNextKeyCMBool;
					bBooleanMode = true;
					myParser.nNextKeyCMBool = myParser.nNextKeyCMBool*2;
				}
				
				if (bBooleanMode) {
					callDebugInfo(IRCParser.DEBUG_INFO, "Boolean Mode: %c [%d] {Positive: %b}",cMode, nValue, bPositive);
					
					if (bPositive) { nCurrent = nCurrent | nValue; }
					else { nCurrent = nCurrent ^ (nCurrent & nValue); }
				} else {
					
					if (bPositive || nValue == IRCParser.MODE_LIST || ((nValue & IRCParser.MODE_UNSET) == IRCParser.MODE_UNSET)) {
						if (sModestr.length <= nParam) {
							myParser.callErrorInfo(new ParserError(ParserError.ERROR_FATAL, "Broken Modes. Parameter required but not given.", myParser.getLastLine()));
						}
					}
					
					
					if (nValue == IRCParser.MODE_LIST) {
						// List Mode
						sModeParam = sModestr[nParam++];
						sNonUserModeStrParams = sNonUserModeStrParams+" "+sModeParam;
						nTemp = (Calendar.getInstance().getTimeInMillis() / 1000);
						iChannel.setListModeParam(cMode, new ChannelListModeItem(sModeParam, token[0], nTemp ), bPositive);
						callDebugInfo(IRCParser.DEBUG_INFO, "List Mode: %c [%s] {Positive: %b}",cMode, sModeParam, bPositive);
						if (cbSingle != null) { cbSingle.call(iChannel, setterCCI, token[0], cPositive+cMode+" "+sModeParam ); }
					} else {
						// Mode with a parameter
						if (bPositive) { 
							// +Mode - always needs a parameter to set
							sModeParam = sModestr[nParam++];
							sNonUserModeStrParams = sNonUserModeStrParams+" "+sModeParam;
							callDebugInfo(IRCParser.DEBUG_INFO, "Set Mode: %c [%s] {Positive: %b}",cMode, sModeParam, bPositive);
							iChannel.setModeParam(cMode,sModeParam);
							if (cbSingle != null) { cbSingle.call(iChannel, setterCCI, token[0], cPositive+cMode+" "+sModeParam ); }
						} else {
							// -Mode - parameter isn't always needed, we need to check
							if ((nValue & IRCParser.MODE_UNSET) == IRCParser.MODE_UNSET) {
{'fraction_non_alphanumeric': 0.1183591123066577, 'fraction_numerical': 0.006444743331091684, 'mean_word_length': 4.073926642024452, 'pattern_counts': {'":': 2, '<': 13, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4924182', 'n_tokens_mistral': 6552, 'n_tokens_neox': 5528, 'n_words': 1453}