<commit_before> /*
  * Copyright 2012 <NAME>
  *
  * Licensed 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 de.jpaw.bonaparte.dsl.generator.java

import de.jpaw.bonaparte.dsl.bonScript.FieldDefinition
import de.jpaw.bonaparte.dsl.bonScript.ClassDefinition
import static extension de.jpaw.bonaparte.dsl.generator.XUtil.*
import de.jpaw.bonaparte.dsl.generator.DataTypeExtension
import de.jpaw.bonaparte.dsl.generator.DataCategory
import org.apache.log4j.Logger
import java.util.Map
import de.jpaw.bonaparte.dsl.bonScript.XXmlAccess

class JavaCompare {
    private static final Logger LOGGER = Logger.getLogger(JavaCompare)
    private static final Map<String,String> JAVA_PRIMITIVE_TO_WRAPPER = #{
        "byte" -> "Byte",
        "short" -> "Short",
        "int" -> "Integer",
        "long" -> "Long",
        "float" -> "Float",
        "double" -> "Double",
        "boolean" -> "Boolean",
        "char" -> "Character"
    }

    def private static writeCompareToField(ClassDefinition d, FieldDefinition f) {
        val ref = DataTypeExtension::get(f.datatype)
        if (!f.isRequired)
            LOGGER.error("Field " + f.name + " of class " + d.name + " is not required, but used as a Comparable criteria")
        if (ref.isPrimitive) {
            // must use the wrapper. use some autoboxing here
            return '''
                _i = «JAVA_PRIMITIVE_TO_WRAPPER.get(f.datatype.getJavaDataType)».valueOf(«f.name»).compareTo(_o.«f.name»);
                if (_i != 0)
                    return _i;
            '''
        } else {
            // is an object anyway
            return '''
                _i = «f.name».compareTo(_o.«f.name»);
                if (_i != 0)
                    return _i;
            '''
        }
    }
    // only invoked if d.orderedByList != null
    def public static writeComparable(ClassDefinition d) '''
        @Override
        public int compareTo(«d.name» _o) {
            int _i;
            «FOR f: d.orderedByList.field»
                «writeCompareToField(d, f)»
            «ENDFOR»
            return 0;  // objects are the same
        }
    '''

    /** Creates a comparison of two fields (same members of different class instances) when the first is known not to be null. */
    def private static writeCompareSub(FieldDefinition i, DataTypeExtension ref, String index, String tindex) {
        if (ref.category == DataCategory::OBJECT) {
            if (ref.objectDataType?.externalType !== null) {
                // external type. use equals() or compareTo()
                if (ref.objectDataType.useCompareToInsteadOfEquals)
                    // FIXME: there is an issue here, as elements of Lists will always be compared using equals(), also for types where we want to use compareTo()!
                    return '''«index».compareTo(«tindex») == 0'''
                else
                    return '''«index».equals(«tindex»)'''
            } else {
                // regular bonaportable. use "sameContents" method
                return '''«index».equals(«tindex»)'''
            }
        }
        // not object. treat elementary data types next
        switch (getJavaDataType(i.datatype)) {
        case "byte []":     '''Arrays.equals(«index», «tindex»)'''
        case "BigDecimal":  '''(«tindex» != null && «index».compareTo(«tindex») == 0)'''
        // case "BigDecimal":  '''BigDecimalTools.equals(«index», «ref.elementaryDataType.decimals», «tindex», «ref.elementaryDataType.decimals»)'''     // was: «index».compareTo(«tindex») == 0'''
        // case "Double":      '''«index».compareTo(«tindex») == 0''' // difference to equals is for NaN values
        // case "Float":       '''«index».compareTo(«tindex») == 0''' // difference to equals is for NaN values
        default:            '''«index».equals(«tindex»)'''
        }
    }

    /** Creates a comparison of two fields (same members of different class instances) when both can be null. */
    def public static doCompareWithNull(String a, String b, CharSequence complex) {
        return '''(«a» == null ? «b» == null : «complex»)'''
    }

    /** Creates a comparison of two fields (same members of different class instances) when both can be null, for a specific instance (array index / Map/List/Set member). */
    def private static writeCompareStuff(FieldDefinition i, String index, String tindex, String end) {
        val ref = DataTypeExtension::get(i.datatype)
        return '''
            «IF ref.isPrimitive»
                «index» == «tindex»«end»
            «ELSE»
                «doCompareWithNull(index, tindex, writeCompareSub(i, ref, index, tindex))»«end»
            «ENDIF»
        '''
    }

    /** Write a hashcode expression for a single primitive field. */
    def public static writePrimitiveSimpleHash(FieldDefinition i, DataTypeExtension ref) {
        switch (ref.javaType) {
        case "Float":   '''(Float.valueOf(«i.name»).hashCode())'''
        case "Double":  '''(Double.valueOf(«i.name»).hashCode())'''
        case "Boolean": '''(«i.name» ? 1231 : 1237)'''  // as in Boolean.hashCode() according to Java specs
        case "Long":    '''(int)(«i.name»^(«i.name»>>>32))'''  // as in Java Long<commit_msg>tuning: do not allocate temporary object to compute the hash code of a double or float member<commit_after> /*
  * Copyright 2012 <NAME>
  *
  * Licensed 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 de.jpaw.bonaparte.dsl.generator.java

import de.jpaw.bonaparte.dsl.bonScript.FieldDefinition
import de.jpaw.bonaparte.dsl.bonScript.ClassDefinition
import static extension de.jpaw.bonaparte.dsl.generator.XUtil.*
import de.jpaw.bonaparte.dsl.generator.DataTypeExtension
import de.jpaw.bonaparte.dsl.generator.DataCategory
import org.apache.log4j.Logger
import java.util.Map
import de.jpaw.bonaparte.dsl.bonScript.XXmlAccess

class JavaCompare {
    private static final Logger LOGGER = Logger.getLogger(JavaCompare)
    private static final Map<String,String> JAVA_PRIMITIVE_TO_WRAPPER = #{
        "byte" -> "Byte",
        "short" -> "Short",
        "int" -> "Integer",
        "long" -> "Long",
        "float" -> "Float",
        "double" -> "Double",
        "boolean" -> "Boolean",
        "char" -> "Character"
    }

    def private static writeCompareToField(ClassDefinition d, FieldDefinition f) {
        val ref = DataTypeExtension::get(f.datatype)
        if (!f.isRequired)
            LOGGER.error("Field " + f.name + " of class " + d.name + " is not required, but used as a Comparable criteria")
        if (ref.isPrimitive) {
            // must use the wrapper. use some autoboxing here
            return '''
                _i = «JAVA_PRIMITIVE_TO_WRAPPER.get(f.datatype.getJavaDataType)».valueOf(«f.name»).compareTo(_o.«f.name»);
                if (_i != 0)
                    return _i;
            '''
        } else {
            // is an object anyway
            return '''
                _i = «f.name».compareTo(_o.«f.name»);
                if (_i != 0)
                    return _i;
            '''
        }
    }
    // only invoked if d.orderedByList != null
    def public static writeComparable(ClassDefinition d) '''
        @Override
        public int compareTo(«d.name» _o) {
            int _i;
            «FOR f: d.orderedByList.field»
                «writeCompareToField(d, f)»
            «ENDFOR»
            return 0;  // objects are the same
        }
    '''

    /** Creates a comparison of two fields (same members of different class instances) when the first is known not to be null. */
    def private static writeCompareSub(FieldDefinition i, DataTypeExtension ref, String index, String tindex) {
        if (ref.category == DataCategory::OBJECT) {
            if (ref.objectDataType?.externalType !== null) {
                // external type. use equals() or compareTo()
                if (ref.objectDataType.useCompareToInsteadOfEquals)
                    // FIXME: there is an issue here, as elements of Lists will always be compared using equals(), also for types where we want to use compareTo()!
                    return '''«index».compareTo(«tindex») == 0'''
                else
                    return '''«index».equals(«tindex»)'''
            } else {
                // regular bonaportable. use "sameContents" method
                return '''«index».equals(«tindex»)'''
            }
        }
        // not object. treat elementary data types next
        switch (getJavaDataType(i.datatype)) {
        case "byte []":     '''Arrays.equals(«index», «tindex»)'''
        case "BigDecimal":  '''(«tindex» != null && «index».compareTo(«tindex») == 0)'''
        // case "BigDecimal":  '''BigDecimalTools.equals(«index», «ref.elementaryDataType.decimals», «tindex», «ref.elementaryDataType.decimals»)'''     // was: «index».compareTo(«tindex») == 0'''
        // case "Double":      '''«index».compareTo(«tindex») == 0''' // difference to equals is for NaN values
        // case "Float":       '''«index».compareTo(«tindex») == 0''' // difference to equals is for NaN values
        default:            '''«index».equals(«tindex»)'''
        }
    }

    /** Creates a comparison of two fields (same members of different class instances) when both can be null. */
    def public static doCompareWithNull(String a, String b, CharSequence complex) {
        return '''(«a» == null ? «b» == null : «complex»)'''
    }

    /** Creates a comparison of two fields (same members of different class instances) when both can be null, for a specific instance (array index / Map/List/Set member). */
    def private static writeCompareStuff(FieldDefinition i, String index, String tindex, String end) {
        val ref = DataTypeExtension::get(i.datatype)
        return '''
            «IF ref.isPrimitive»
                «index» == «tindex»«end»
            «ELSE»
                «doCompareWithNull(index, tindex, writeCompareSub(i, ref, index, tindex))»«end»
            «ENDIF»
        '''
    }

    /** Write a hashcode expression for a single primitive field. */
    def public static writePrimitiveSimpleHash(FieldDefinition i, DataTypeExtension ref) {
        switch (ref.javaType) {
        case "Float":   '''Float.hashCode(«i.name»)'''
        case "Double":  '''Double.hashCode(«i.name»)'''
        case "Boolean": '''(«i.name» ? 1231 : 1237)'''  // as in Boolean.hashCode() according to Java specs
        case "Long":    '''(int)(«i.name»^(«i.name»>>>32))'''  // as in Java Long
{'fraction_non_alphanumeric': 0.129236919867895, 'fraction_numerical': 0.004693203545976013, 'mean_word_length': 2.357747300846221, 'pattern_counts': {'":': 18, '<': 7, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, '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': '3023933', 'n_tokens_mistral': 3349, 'n_tokens_neox': 3260, 'n_words': 1235}