<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.isis.applib.services.appfeat;

import java.io.Serializable;
import java.util.Comparator;
import java.util.function.Consumer;

import static java.util.Comparator.comparing;
import static java.util.Comparator.naturalOrder;
import static java.util.Comparator.nullsFirst;

import javax.annotation.Nullable;

import org.apache.isis.applib.Identifier;
import org.apache.isis.applib.annotation.Programmatic;
import org.apache.isis.applib.annotation.Value;
import org.apache.isis.applib.util.Equality;
import org.apache.isis.applib.util.Hashing;
import org.apache.isis.applib.util.ObjectContracts;
import org.apache.isis.applib.util.TitleBuffer;
import org.apache.isis.applib.util.ToString;
import org.apache.isis.commons.collections.Can;
import org.apache.isis.commons.internal.assertions._Assert;
import org.apache.isis.commons.internal.base._Strings;
import org.apache.isis.commons.internal.collections._Lists;
import org.apache.isis.commons.internal.exceptions._Exceptions;

import lombok.Getter;
import lombok.NonNull;
import lombok.Synchronized;
import lombok.val;

/**
 * Value type representing a namespace, type or member.
 * <p>
 * This value is {@link Comparable}, the implementation of which considers
 * {@link #getSort() (feature) sort}, {@link #getNamespace() namespace},
 * {@link #getTypeSimpleName() type simple name} and {@link #getLogicalMemberName() member-logical-name}.
 * <p>
 * If the represented member is an <i>action</i>, then {@link #getLogicalMemberName() member-logical-name}
 * must <b>not</b> include any parameter list or parentheses.
 * Consequently method overloading is not supported.
 * <p>
 * If there is a member name clash involving an <i>action</i> and an <i>association</i>,
 * then consequently any permissions defined automatically apply to both and one cannot separate
 * these.
 *
 * @since 1.x revised for 2.0 {@index}
 */
@Value
public class ApplicationFeatureId
implements
    Comparable<ApplicationFeatureId>,
    Serializable {

    private static final long serialVersionUID = 1L;

    // -- CONSTANTS

    public static final ApplicationFeatureId NAMESPACE_DEFAULT = newNamespace("default");

    // -- FACTORY METHODS

    public static ApplicationFeatureId fromIdentifier(final @NonNull Identifier identifier) {

        val logicalTypeName = identifier.getLogicalTypeName();

        if(identifier.getType().isClass()) {
            return newType(logicalTypeName);
        }
        return newMember(logicalTypeName, identifier.getMemberLogicalName());
    }

    public static ApplicationFeatureId newFeature(
            final @NonNull ApplicationFeatureSort featureSort,
            final @NonNull String qualifiedName) {

        switch (featureSort) {
        case NAMESPACE:
            return newNamespace(qualifiedName);
        case TYPE:
            return newType(qualifiedName);
        case MEMBER:
            return newMember(qualifiedName);
        }
        throw _Exceptions.illegalArgument("Unknown feature sort '%s'", featureSort);
    }

    public static ApplicationFeatureId newFeature(
            final @NonNull  String namespace,
            final @Nullable String logicalTypeSimpleName,
            final @Nullable String memberName) {

        if(logicalTypeSimpleName == null) {
            return newNamespace(namespace);
        }
        val logicalTypeName = namespace + "." + logicalTypeSimpleName;
        if(memberName == null) {
            return newType(logicalTypeName);
        }
        return newMember(logicalTypeName, memberName);
    }

    public static ApplicationFeatureId newNamespace(final String namespace) {
        val featureId = new ApplicationFeatureId(ApplicationFeatureSort.NAMESPACE);
        featureId.namespace = namespace;
        featureId.typeSimpleName = null;
        featureId.logicalMemberName = null;
        return featureId;
    }

    public static ApplicationFeatureId newType(final String logicalTypeName) {
        val featureId = new ApplicationFeatureId(ApplicationFeatureSort.TYPE);
        initType(featureId, logicalTypeName);
        return featureId;
    }

    public static ApplicationFeatureId newMember(final String logicalTypeName, final String memberLogicalName) {
        val featureId = new ApplicationFeatureId(ApplicationFeatureSort.MEMBER);
        initType(featureId, logicalTypeName);
        initMember(featureId, memberLogicalName);
        return featureId;
    }

    public static ApplicationFeatureId newMember(String fullyQualifiedName) {
        val featureId = new ApplicationFeatureId(ApplicationFeatureSort.MEMBER);
        final int i = fullyQualifiedName.lastIndexOf("#");
        if(i == -1) {
            throw new IllegalArgumentException("Malformed, expected a '#': " + fullyQualifiedName);<commit_msg>ISIS-2772: adds new factory method for ApplicationFeatureId, using LogicalType<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.isis.applib.services.appfeat;

import java.io.Serializable;
import java.util.Comparator;
import java.util.function.Consumer;

import static java.util.Comparator.comparing;
import static java.util.Comparator.naturalOrder;
import static java.util.Comparator.nullsFirst;

import javax.annotation.Nullable;

import org.apache.isis.applib.Identifier;
import org.apache.isis.applib.annotation.Programmatic;
import org.apache.isis.applib.annotation.Value;
import org.apache.isis.applib.util.Equality;
import org.apache.isis.applib.util.Hashing;
import org.apache.isis.applib.util.ObjectContracts;
import org.apache.isis.applib.util.TitleBuffer;
import org.apache.isis.applib.util.ToString;
import org.apache.isis.commons.collections.Can;
import org.apache.isis.commons.internal.assertions._Assert;
import org.apache.isis.commons.internal.base._Strings;
import org.apache.isis.commons.internal.collections._Lists;
import org.apache.isis.commons.internal.exceptions._Exceptions;

import lombok.Getter;
import lombok.NonNull;
import lombok.Synchronized;
import lombok.val;

/**
 * Value type representing a namespace, type or member.
 * <p>
 * This value is {@link Comparable}, the implementation of which considers
 * {@link #getSort() (feature) sort}, {@link #getNamespace() namespace},
 * {@link #getTypeSimpleName() type simple name} and {@link #getLogicalMemberName() member-logical-name}.
 * <p>
 * If the represented member is an <i>action</i>, then {@link #getLogicalMemberName() member-logical-name}
 * must <b>not</b> include any parameter list or parentheses.
 * Consequently method overloading is not supported.
 * <p>
 * If there is a member name clash involving an <i>action</i> and an <i>association</i>,
 * then consequently any permissions defined automatically apply to both and one cannot separate
 * these.
 *
 * @since 1.x revised for 2.0 {@index}
 */
@Value
public class ApplicationFeatureId
implements
    Comparable<ApplicationFeatureId>,
    Serializable {

    private static final long serialVersionUID = 1L;

    // -- CONSTANTS

    public static final ApplicationFeatureId NAMESPACE_DEFAULT = newNamespace("default");

    // -- FACTORY METHODS

    public static ApplicationFeatureId fromIdentifier(final @NonNull Identifier identifier) {

        val logicalTypeName = identifier.getLogicalTypeName();

        if(identifier.getType().isClass()) {
            return newType(logicalTypeName);
        }
        return newMember(logicalTypeName, identifier.getMemberLogicalName());
    }

    public static ApplicationFeatureId newFeature(
            final @NonNull ApplicationFeatureSort featureSort,
            final @NonNull String qualifiedName) {

        switch (featureSort) {
        case NAMESPACE:
            return newNamespace(qualifiedName);
        case TYPE:
            return newType(qualifiedName);
        case MEMBER:
            return newMember(qualifiedName);
        }
        throw _Exceptions.illegalArgument("Unknown feature sort '%s'", featureSort);
    }

    public static ApplicationFeatureId newFeature(
            final @NonNull  String namespace,
            final @Nullable String logicalTypeSimpleName,
            final @Nullable String memberName) {

        if(logicalTypeSimpleName == null) {
            return newNamespace(namespace);
        }
        val logicalTypeName = namespace + "." + logicalTypeSimpleName;
        if(memberName == null) {
            return newType(logicalTypeName);
        }
        return newMember(logicalTypeName, memberName);
    }

    public static ApplicationFeatureId newNamespace(final String namespace) {
        val featureId = new ApplicationFeatureId(ApplicationFeatureSort.NAMESPACE);
        featureId.namespace = namespace;
        featureId.typeSimpleName = null;
        featureId.logicalMemberName = null;
        return featureId;
    }

    public static ApplicationFeatureId newType(final String logicalTypeName) {
        val featureId = new ApplicationFeatureId(ApplicationFeatureSort.TYPE);
        initType(featureId, logicalTypeName);
        return featureId;
    }

    public static ApplicationFeatureId newType(final LogicalType logicalType) {
        return newType(logicalType.getLogicalTypeName());
    }

    public static ApplicationFeatureId newMember(final String logicalTypeName, final String memberLogicalName) {
        val featureId = new ApplicationFeatureId(ApplicationFeatureSort.MEMBER);
        initType(featureId, logicalTypeName);
        initMember(featureId, memberLogicalName);
        return featureId;
    }

    public static ApplicationFeatureId newMember(String fullyQualifiedName) {
        val featureId = new ApplicationFeatureId(ApplicationFeatureSort.MEMBER);
        final int i = fullyQualifiedName.lastIndexOf("#");
        if(i == -1) {
            throw new IllegalArgumentException("Malformed, expected a '#': " + fullyQualifiedName);
{'fraction_non_alphanumeric': 0.08490401396160559, 'fraction_numerical': 0.0019197207678883072, 'mean_word_length': 3.6513798701298703, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 27, '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': '2774577', 'n_tokens_mistral': 3041, 'n_tokens_neox': 2991, 'n_words': 885}