<commit_before>/****************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one   *
 * or more contributor license agreements.  See the NOTICE file *
 * distributed with this work for additional information        *
 * regarding copyright ownership.  The ASF licenses this file   *
 * to you under the Apache License, Version 2.0 (the            *
 * "License"); you may not use this file except in compliance   *
 * with the License.  You may obtain a copy of the License at   *
 *                                                              *
 *   http://www.apache.org/licenses/LICENSE-2.0                 *
 *                                                              *
 * Unless required by applicable law or agreed to in writing,   *
 * software distributed under the License is distributed on an  *
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
 * KIND, either express or implied.  See the License for the    *
 * specific language governing permissions and limitations      *
 * under the License.                                           *
 ****************************************************************/

package org.apache.james.imapserver.codec.decode;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;

import javax.mail.Flags;

import org.apache.james.api.imap.ProtocolException;
import org.apache.james.api.imap.message.MessageFlags;

/**
 * Utility procedures.
 */
public final class DecoderUtils {

    private static final int ASCII_ZERO = '0';
    
    private static final int JAN_BIT = 0x1;
    private static final int FEB_BIT = 0x2;
    private static final int MAR_BIT = 0x4;
    private static final int APR_BIT = 0x8;
    private static final int MAY_BIT = 0x10;
    private static final int JUN_BIT = 0x20;
    private static final int JUL_BIT = 0x40;
    private static final int AUG_BIT = 0x80;
    private static final int SEP_BIT = 0x100;
    private static final int OCT_BIT = 0x200;
    private static final int NOV_BIT = 0x400;
    private static final int DEC_BIT = 0x800;
    private static final int ALL_MONTH_BITS = JAN_BIT | FEB_BIT | MAR_BIT | APR_BIT | MAY_BIT | JUN_BIT 
                                        | JUL_BIT | AUG_BIT | SEP_BIT | OCT_BIT | NOV_BIT | DEC_BIT;
    
    public static void setFlag( final String flagString, final Flags flags )  {
        if ( flagString.equalsIgnoreCase( MessageFlags.ANSWERED ) ) {
            flags.add(Flags.Flag.ANSWERED);
        }
        else if ( flagString.equalsIgnoreCase( MessageFlags.DELETED ) ) {
            flags.add(Flags.Flag.DELETED);
        }
        else if ( flagString.equalsIgnoreCase( MessageFlags.DRAFT ) ) {
            flags.add(Flags.Flag.DRAFT);
        }
        else if ( flagString.equalsIgnoreCase( MessageFlags.FLAGGED ) ) {
            flags.add(Flags.Flag.FLAGGED);
        }
        else if ( flagString.equalsIgnoreCase( MessageFlags.SEEN ) ) {
            flags.add(Flags.Flag.SEEN);
        } else {
            if ( flagString.equalsIgnoreCase( MessageFlags.RECENT) ) {
                // RFC3501 specifically excludes /Recent 
                // The /Recent flag should be set automatically by the server
            } else {
                // RFC3501 allows novel flags
                flags.add(flagString);
            }
        }
    }

    /**
     * Decodes the given string as a standard IMAP date-time.
     * @param dateString standard IMAP date-time
     * @return <code>Date</code> with time component, not null
     * @throws ProtocolException when this conversion fails
     */
    public static final Date decodeDateTime(CharSequence chars) throws ProtocolException {
        if (isDateTime(chars)) {
            final char dayHigh = chars.charAt(0);
            final char dayLow = chars.charAt(1);
            final int day = decodeFixedDay(dayHigh, dayLow);
            
            final char monthFirstChar = chars.charAt(3);
            final char monthSecondChar = chars.charAt(4);
            final char monthThirdChar = chars.charAt(5);
            final int month = decodeMonth(monthFirstChar, monthSecondChar, monthThirdChar);
            
            final char milleniumChar = chars.charAt(7);
            final char centuryChar = chars.charAt(8);
            final char decadeChar = chars.charAt(9);
            final char yearChar = chars.charAt(10);
            final int year = decodeYear(milleniumChar, centuryChar, decadeChar, yearChar);
            
            final char zoneDeterminent = chars.charAt(21);
            final char zoneDigitOne = chars.charAt(22);
            final char zoneDigitTwo = chars.charAt(23);
            final char zoneDigitThree = chars.charAt(24);
            final char zoneDigitFour = chars.charAt(25);
            final int offset = decodeZone(zoneDeterminent, zoneDigitOne, zoneDigitTwo, zoneDigitThree, zoneDigitFour);
            
            final char hourHigh = chars.charAt(12);
            final char hourLow = chars.charAt(13);
            final int hour = applyHourOffset(offset, decodeNumber(hourHigh, hourLow));
            
            final char minuteHigh = chars.charAt(15);
            final char minuteLow = chars.charAt(16);
            final int minute = applyMinuteOffset(offset, decodeNumber(minuteHigh, minuteLow));
            
            final char secondHigh = chars.charAt(18);
            final char secondLow = chars.charAt(19);
            final int second = decodeNumber(secondHigh, secondLow);
                    
            final GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("GMT"), Locale.US);
            calendar.clear();
            calendar.set(year, month, day, hour, minute, second);
            final Date result = calendar.getTime();
            return result;
        } else {
            final String message;
            if (chars == null) {
                message = "Expected a date-time but was nothing.";
            } else {
                message =  new StringBuffer("Expected a date-time but was "). append(chars).toString();
            }
                
            throw new ProtocolException(message);
        }
        
    }
    
    private static boolean isDateTime(CharSequence chars) {
        final boolean result;
        if (chars == null) {
            result = false;
        } else if (chars.length() < 20) {
            // Be liberal in what you accept
            result = false;
        } else {
            result =true;
        }
        return result;
    }

    private static int applyMinuteOffset(final int offset, final int minutes) {
        final int result =  minutes - ((Math.abs(offset) % 100) * (int) Math.signum(offset));
        return result;
    }
    
    private static int applyHourOffset(final int offset, final int hours) {<commit_msg>Java 1.4 compatibility: Math.signum is a java 5+ method. Replaced with backward compatible code.<commit_after>/****************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one   *
 * or more contributor license agreements.  See the NOTICE file *
 * distributed with this work for additional information        *
 * regarding copyright ownership.  The ASF licenses this file   *
 * to you under the Apache License, Version 2.0 (the            *
 * "License"); you may not use this file except in compliance   *
 * with the License.  You may obtain a copy of the License at   *
 *                                                              *
 *   http://www.apache.org/licenses/LICENSE-2.0                 *
 *                                                              *
 * Unless required by applicable law or agreed to in writing,   *
 * software distributed under the License is distributed on an  *
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
 * KIND, either express or implied.  See the License for the    *
 * specific language governing permissions and limitations      *
 * under the License.                                           *
 ****************************************************************/

package org.apache.james.imapserver.codec.decode;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;

import javax.mail.Flags;

import org.apache.james.api.imap.ProtocolException;
import org.apache.james.api.imap.message.MessageFlags;

/**
 * Utility procedures.
 */
public final class DecoderUtils {

    private static final int ASCII_ZERO = '0';
    
    private static final int JAN_BIT = 0x1;
    private static final int FEB_BIT = 0x2;
    private static final int MAR_BIT = 0x4;
    private static final int APR_BIT = 0x8;
    private static final int MAY_BIT = 0x10;
    private static final int JUN_BIT = 0x20;
    private static final int JUL_BIT = 0x40;
    private static final int AUG_BIT = 0x80;
    private static final int SEP_BIT = 0x100;
    private static final int OCT_BIT = 0x200;
    private static final int NOV_BIT = 0x400;
    private static final int DEC_BIT = 0x800;
    private static final int ALL_MONTH_BITS = JAN_BIT | FEB_BIT | MAR_BIT | APR_BIT | MAY_BIT | JUN_BIT 
                                        | JUL_BIT | AUG_BIT | SEP_BIT | OCT_BIT | NOV_BIT | DEC_BIT;
    
    public static void setFlag( final String flagString, final Flags flags )  {
        if ( flagString.equalsIgnoreCase( MessageFlags.ANSWERED ) ) {
            flags.add(Flags.Flag.ANSWERED);
        }
        else if ( flagString.equalsIgnoreCase( MessageFlags.DELETED ) ) {
            flags.add(Flags.Flag.DELETED);
        }
        else if ( flagString.equalsIgnoreCase( MessageFlags.DRAFT ) ) {
            flags.add(Flags.Flag.DRAFT);
        }
        else if ( flagString.equalsIgnoreCase( MessageFlags.FLAGGED ) ) {
            flags.add(Flags.Flag.FLAGGED);
        }
        else if ( flagString.equalsIgnoreCase( MessageFlags.SEEN ) ) {
            flags.add(Flags.Flag.SEEN);
        } else {
            if ( flagString.equalsIgnoreCase( MessageFlags.RECENT) ) {
                // RFC3501 specifically excludes /Recent 
                // The /Recent flag should be set automatically by the server
            } else {
                // RFC3501 allows novel flags
                flags.add(flagString);
            }
        }
    }

    /**
     * Decodes the given string as a standard IMAP date-time.
     * @param dateString standard IMAP date-time
     * @return <code>Date</code> with time component, not null
     * @throws ProtocolException when this conversion fails
     */
    public static final Date decodeDateTime(CharSequence chars) throws ProtocolException {
        if (isDateTime(chars)) {
            final char dayHigh = chars.charAt(0);
            final char dayLow = chars.charAt(1);
            final int day = decodeFixedDay(dayHigh, dayLow);
            
            final char monthFirstChar = chars.charAt(3);
            final char monthSecondChar = chars.charAt(4);
            final char monthThirdChar = chars.charAt(5);
            final int month = decodeMonth(monthFirstChar, monthSecondChar, monthThirdChar);
            
            final char milleniumChar = chars.charAt(7);
            final char centuryChar = chars.charAt(8);
            final char decadeChar = chars.charAt(9);
            final char yearChar = chars.charAt(10);
            final int year = decodeYear(milleniumChar, centuryChar, decadeChar, yearChar);
            
            final char zoneDeterminent = chars.charAt(21);
            final char zoneDigitOne = chars.charAt(22);
            final char zoneDigitTwo = chars.charAt(23);
            final char zoneDigitThree = chars.charAt(24);
            final char zoneDigitFour = chars.charAt(25);
            final int offset = decodeZone(zoneDeterminent, zoneDigitOne, zoneDigitTwo, zoneDigitThree, zoneDigitFour);
            
            final char hourHigh = chars.charAt(12);
            final char hourLow = chars.charAt(13);
            final int hour = applyHourOffset(offset, decodeNumber(hourHigh, hourLow));
            
            final char minuteHigh = chars.charAt(15);
            final char minuteLow = chars.charAt(16);
            final int minute = applyMinuteOffset(offset, decodeNumber(minuteHigh, minuteLow));
            
            final char secondHigh = chars.charAt(18);
            final char secondLow = chars.charAt(19);
            final int second = decodeNumber(secondHigh, secondLow);
                    
            final GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("GMT"), Locale.US);
            calendar.clear();
            calendar.set(year, month, day, hour, minute, second);
            final Date result = calendar.getTime();
            return result;
        } else {
            final String message;
            if (chars == null) {
                message = "Expected a date-time but was nothing.";
            } else {
                message =  new StringBuffer("Expected a date-time but was "). append(chars).toString();
            }
                
            throw new ProtocolException(message);
        }
        
    }
    
    private static boolean isDateTime(CharSequence chars) {
        final boolean result;
        if (chars == null) {
            result = false;
        } else if (chars.length() < 20) {
            // Be liberal in what you accept
            result = false;
        } else {
            result =true;
        }
        return result;
    }

    private static int applyMinuteOffset(final int offset, final int minutes) {
        final int result =  minutes - ((Math.abs(offset) % 100) * (offset == 0 ? 0 : offset > 0 ? 1 : -1));
        return result;
    }
    
    private static int applyHourOffset(final int offset, final int hours) {
{'fraction_non_alphanumeric': 0.09754526586989193, 'fraction_numerical': 0.012881986688613754, 'mean_word_length': 2.0530915446799214, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 14, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3638446', 'n_tokens_mistral': 3798, 'n_tokens_neox': 3560, 'n_words': 1124}