<commit_before>/*
 * Copyright 2000-2009 JetBrains s.r.o.
 *
 * 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 com.intellij.lang;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeRegistry;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.util.NotNullLazyValue;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ConcurrentMultiMap;
import com.intellij.util.containers.MultiMap;
import consulo.annotations.Exported;
import consulo.annotations.Immutable;
import consulo.lang.LanguageVersion;
import consulo.lang.LanguageVersionDefines;
import consulo.util.pointers.Named;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * The base class for all programming language support implementations. Specific language implementations should inherit from this class
 * and its register instance wrapped with {@link com.intellij.openapi.fileTypes.LanguageFileType} instance through
 * <code>FileTypeManager.getInstance().registerFileType</code>
 * There should be exactly one instance of each Language.
 * It is usually created when creating {@link com.intellij.openapi.fileTypes.LanguageFileType} and can be retrieved later
 * with {@link #findInstance(Class)}.
 */
public abstract class Language extends UserDataHolderBase implements Named {
  private static final Logger LOG = Logger.getInstance(Language.class);

  private static final Map<Class<? extends Language>, Language> ourRegisteredLanguages = new ConcurrentHashMap<>();
  private static final MultiMap<String, LanguageVersion> ourRegisteredMimeTypes = new ConcurrentMultiMap<>();
  private static final Map<String, Language> ourRegisteredIDs = new THashMap<>();

  private final Language myBaseLanguage;
  private final String myID;
  private final String[] myMimeTypes;

  public static final Language ANY = new Language("") {
    @Override
    public String toString() {
      //noinspection HardCodedStringLiteral
      return "Language: ANY";
    }
  };

  private NotNullLazyValue<LanguageVersion[]> myVersions = new NotNullLazyValue<LanguageVersion[]>() {
    @NotNull
    @Override
    protected LanguageVersion[] compute() {
      LanguageVersion[] versions = findVersions();
      if (versions.length == 0) {
        throw new IllegalArgumentException("Language version is empty for language: " + Language.this);
      }

      for (LanguageVersion version : versions) {
        for (String mimeType : version.getMimeTypes()) {
          ourRegisteredMimeTypes.putValue(mimeType, version);
        }
      }

      String[] mimeTypes = Language.this.getMimeTypes();
      for (String mimeType : mimeTypes) {
        for (LanguageVersion version : versions) {
          ourRegisteredMimeTypes.putValue(mimeType, version);
        }
      }

      return versions;
    }
  };

  protected Language(@NotNull @NonNls String id) {
    this(id, ArrayUtil.EMPTY_STRING_ARRAY);
  }

  protected Language(@NotNull @NonNls final String ID, @NotNull @NonNls final String... mimeTypes) {
    this(null, ID, mimeTypes);
  }

  protected Language(@Nullable Language baseLanguage, @NotNull @NonNls final String ID, @NotNull @NonNls final String... mimeTypes) {
    myBaseLanguage = baseLanguage;
    myID = ID;
    myMimeTypes = mimeTypes;
    Class<? extends Language> langClass = getClass();
    Language prev = ourRegisteredLanguages.put(langClass, this);
    if (prev != null) {
      LOG.error("Language of '" + langClass + "' is already registered: " + prev);
      return;
    }
    prev = ourRegisteredIDs.put(ID, this);
    if (prev != null) {
      LOG.error("Language with ID '" + ID + "' is already registered: " + prev.getClass());
    }
  }

  /**
   * Fake language identifier without registering
   */
  protected Language(String id, @SuppressWarnings("UnusedParameters") boolean register) {
    myID = id;
    myBaseLanguage = null;
    myMimeTypes = ArrayUtil.EMPTY_STRING_ARRAY;
  }

  /**
   * @return collection of all languages registered so far.
   */
  @NotNull
  public static Collection<Language> getRegisteredLanguages() {
    return new ArrayList<>(ourRegisteredLanguages.values());
  }

  /**
   * @param klass <code>java.lang.Class</code> of the particular language. Serves key purpose.
   * @return instance of the <code>klass</code> language registered if any.
   */
  @Nullable
  public static <T extends Language> T findInstance(Class<T> klass) {
    //noinspection unchecked
    return (T)ourRegisteredLanguages.get(klass);
  }

  /**
   * @param mimeType of the particular language.
   * @return collection of all languages for the given <code>mimeType</code>.
   */
  @NotNull
  @Exported
  public static Collection<Language> findInstancesByMimeType(@Nullable String mimeType) {
    Collection<LanguageVersion> versions = ourRegisteredMimeTypes.get(mimeType);
    Set<Language> languages = new HashSet<>();
    for (LanguageVersion version : versions) {
      languages.add(version.getLanguage());
    }
    return languages;
  }

  @NotNull
  @Exported
  public static Collection<LanguageVersion> findVersionsByMimeType(@Nullable String mimeType) {
    Collection<LanguageVersion> versions = ourRegisteredMimeTypes.get(mimeType);
    return new ArrayList<>(versions);<commit_msg>npe fix<commit_after>/*
 * Copyright 2000-2009 JetBrains s.r.o.
 *
 * 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 com.intellij.lang;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeRegistry;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.util.NotNullLazyValue;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ConcurrentMultiMap;
import com.intellij.util.containers.MultiMap;
import consulo.annotations.Exported;
import consulo.annotations.Immutable;
import consulo.lang.LanguageVersion;
import consulo.lang.LanguageVersionDefines;
import consulo.util.pointers.Named;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * The base class for all programming language support implementations. Specific language implementations should inherit from this class
 * and its register instance wrapped with {@link com.intellij.openapi.fileTypes.LanguageFileType} instance through
 * <code>FileTypeManager.getInstance().registerFileType</code>
 * There should be exactly one instance of each Language.
 * It is usually created when creating {@link com.intellij.openapi.fileTypes.LanguageFileType} and can be retrieved later
 * with {@link #findInstance(Class)}.
 */
public abstract class Language extends UserDataHolderBase implements Named {
  private static final Logger LOG = Logger.getInstance(Language.class);

  private static final Map<Class<? extends Language>, Language> ourRegisteredLanguages = new ConcurrentHashMap<>();
  private static final MultiMap<String, LanguageVersion> ourRegisteredMimeTypes = new ConcurrentMultiMap<>();
  private static final Map<String, Language> ourRegisteredIDs = new THashMap<>();

  private final Language myBaseLanguage;
  private final String myID;
  private final String[] myMimeTypes;

  public static final Language ANY = new Language("") {
    @Override
    public String toString() {
      //noinspection HardCodedStringLiteral
      return "Language: ANY";
    }
  };

  private NotNullLazyValue<LanguageVersion[]> myVersions = new NotNullLazyValue<LanguageVersion[]>() {
    @NotNull
    @Override
    protected LanguageVersion[] compute() {
      LanguageVersion[] versions = findVersions();
      if (versions.length == 0) {
        throw new IllegalArgumentException("Language version is empty for language: " + Language.this);
      }

      for (LanguageVersion version : versions) {
        for (String mimeType : version.getMimeTypes()) {
          ourRegisteredMimeTypes.putValue(mimeType, version);
        }
      }

      String[] mimeTypes = Language.this.getMimeTypes();
      for (String mimeType : mimeTypes) {
        for (LanguageVersion version : versions) {
          ourRegisteredMimeTypes.putValue(mimeType, version);
        }
      }

      return versions;
    }
  };

  protected Language(@NotNull @NonNls String id) {
    this(id, ArrayUtil.EMPTY_STRING_ARRAY);
  }

  protected Language(@NotNull @NonNls final String ID, @NotNull @NonNls final String... mimeTypes) {
    this(null, ID, mimeTypes);
  }

  protected Language(@Nullable Language baseLanguage, @NotNull @NonNls final String ID, @NotNull @NonNls final String... mimeTypes) {
    myBaseLanguage = baseLanguage;
    myID = ID;
    myMimeTypes = mimeTypes;
    Class<? extends Language> langClass = getClass();
    Language prev = ourRegisteredLanguages.put(langClass, this);
    if (prev != null) {
      LOG.error("Language of '" + langClass + "' is already registered: " + prev);
      return;
    }
    prev = ourRegisteredIDs.put(ID, this);
    if (prev != null) {
      LOG.error("Language with ID '" + ID + "' is already registered: " + prev.getClass());
    }
  }

  /**
   * Fake language identifier without registering
   */
  protected Language(String id, @SuppressWarnings("UnusedParameters") boolean register) {
    myID = id;
    myBaseLanguage = null;
    myMimeTypes = ArrayUtil.EMPTY_STRING_ARRAY;
  }

  /**
   * @return collection of all languages registered so far.
   */
  @NotNull
  public static Collection<Language> getRegisteredLanguages() {
    return new ArrayList<>(ourRegisteredLanguages.values());
  }

  /**
   * @param klass <code>java.lang.Class</code> of the particular language. Serves key purpose.
   * @return instance of the <code>klass</code> language registered if any.
   */
  @Nullable
  public static <T extends Language> T findInstance(Class<T> klass) {
    //noinspection unchecked
    return (T)ourRegisteredLanguages.get(klass);
  }

  /**
   * @param mimeType of the particular language.
   * @return collection of all languages for the given <code>mimeType</code>.
   */
  @NotNull
  @Exported
  public static Collection<Language> findInstancesByMimeType(@Nullable String mimeType) {
    if(mimeType == null) {
      return Collections.emptyList();
    }

    Collection<LanguageVersion> versions = ourRegisteredMimeTypes.get(mimeType);
    Set<Language> languages = new HashSet<>();
    for (LanguageVersion version : versions) {
      languages.add(version.getLanguage());
    }
    return languages;
  }

  @NotNull
  @Exported
  public static Collection<LanguageVersion> findVersionsByMimeType(@Nullable String mimeType) {
    Collection<LanguageVersion> versions = ourRegisteredMimeTypes.get(mimeType);
    return new ArrayList<>(versions);
{'fraction_non_alphanumeric': 0.09839027595269383, 'fraction_numerical': 0.0021353482260183967, 'mean_word_length': 4.1401435204727735, 'pattern_counts': {'":': 0, '<': 61, '<?xml version=': 0, '>': 61, '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': '6021865', 'n_tokens_mistral': 3394, 'n_tokens_neox': 3333, 'n_words': 978}