<commit_before>/*
 * Copyright 2000-2006 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.util.xml.model;

import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.xml.XmlFile;
import com.intellij.util.xml.DomElement;
import com.intellij.util.xml.DomFileElement;
import com.intellij.util.xml.DomManager;
import com.intellij.util.xml.ModelMerger;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;

/**
 * @author <NAME>
 */
public abstract class DomModelFactory<T extends DomElement, M extends DomModel<T>, C extends PsiElement> extends SimpleDomModelFactory<T> {

  private final DomModelCache<M, XmlFile> myModelCache;
  private final DomModelCache<M, Module> myCombinedModelCache;
  private final DomModelCache<List<M>, Module> myAllModelsCache;


  protected DomModelFactory(@NotNull Class<T> aClass,
                          @NotNull ModelMerger modelMerger,
                          final Project project,
                          @NonNls String name) {
    super(aClass, modelMerger);

    myModelCache = new DomModelCache<M, XmlFile>(project, name + " model") {
       @NotNull
       protected CachedValueProvider.Result<M> computeValue(@NotNull XmlFile file) {
         final PsiFile originalFile = file.getOriginalFile();
         if (originalFile != null) {
           file = (XmlFile)originalFile;
         }

         final Module module = ModuleUtil.findModuleForPsiElement(file);
         final M model = module != null ? computeModel(file, module) : null;
         return new CachedValueProvider.Result<M>(model, computeDependencies(file, module));
      }
    };

    myCombinedModelCache = new DomModelCache<M, Module>(project, name + " combined model") {
      @NotNull
      protected CachedValueProvider.Result<M> computeValue(@NotNull final Module module) {
        final M combinedModel = computeCombinedModel(module);
        return new CachedValueProvider.Result<M>(combinedModel, combinedModel == null ? computeDependencies(module) : computeDependencies(combinedModel, module));
      }
    };

    myAllModelsCache = new DomModelCache<List<M>, Module>(project, name + " models list") {
      @NotNull
      protected CachedValueProvider.Result<List<M>> computeValue(@NotNull final Module module) {
        final List<M> models = computeAllModels(module);
        return new CachedValueProvider.Result<List<M>>(models, computeDependencies(module));
      }
    };
  }

  @NotNull
  protected Object[] computeDependencies(@NotNull XmlFile file, @Nullable Module module) {
    if (module == null) {
      return new Object[] {file};
    } else {
      final Object[] moduleDependencies = computeDependencies(module);
      return ArrayUtil.append(moduleDependencies, file);
    }
  }


  @NotNull
  protected Object[] computeDependencies(@NotNull Module module) {
    final ProjectRootManager manager = ProjectRootManager.getInstance(module.getProject());
    return new Object[] { manager };
  }

  @NotNull
  public Object[] computeDependencies(@NotNull M model, @NotNull Module module) {

    final Object[] moduleDependencies = computeDependencies(module);
    final Set<XmlFile> files = model.getConfigFiles();
    final ArrayList<Object> dependencies = new ArrayList<Object>(moduleDependencies.length + files.size());
    dependencies.addAll(files);
    Collections.addAll(dependencies, moduleDependencies);
    return dependencies.toArray(new Object[dependencies.size()]);
  }

  @Nullable
  public abstract M getModel(@NotNull C context);

  @NotNull
  public List<M> getAllModels(@NotNull Module module) {

    final List<M> models = myAllModelsCache.getCachedValue(module);
    if (models == null) {
      return Collections.emptyList();
    }
    else {
      return models;
    }
  }

  @Nullable
  protected abstract List<M> computeAllModels(@NotNull Module module);

  @Nullable
  public M getModelByConfigFile(@Nullable XmlFile psiFile) {
    if (psiFile == null) {
      return null;
    }
    return myModelCache.getCachedValue(psiFile);
  }

  @Nullable
  protected M computeModel(@NotNull XmlFile psiFile, @Nullable Module module) {
    if (module == null) {
      return null;
    }
    final List<M> models = getAllModels(module);
    for (M model: models) {
      final Set<XmlFile> configFiles = model.getConfigFiles();
      if (configFiles.contains(psiFile)) {
        return model;
      }
    }
    return null;
  }

  @Nullable
  public M getCombinedModel(@Nullable Module module) {
    if (module == null) {
      return null;
    }
    return myCombinedModelCache.getCachedValue(module);
  }

  @Nullable
  protected M computeCombinedModel(@NotNull Module module) {
    final List<M> models = getAllModels(module);
    switch (models.size()) {
      case 0:
        return null;
      case 1:
        return models.get(0);
    }
    Set<XmlFile> configFiles = new LinkedHashSet<XmlFile>();
    final ArrayList<T> list = new ArrayList<T>(models.size());
    for (M model: models) {
      final Set<XmlFile> files = model.getConfigFiles();
      for (XmlFile file: files) {
        list.add(getDom(file));
      }
      configFiles.addAll(files);
    }
    final T mergedModel = getModelMerger().mergeModels(getClazz(), list);
    final M firstModel = models.get(0);
    return createCombinedModel(configFiles, mergedModel, firstModel);
  }

  /**
   * Factory method to create combined model for given module.
   * Used by {@link #computeCombinedModel(com.intellij.openapi.module.Module)}.
   *
   * @param configFiles file set including all files for all models returned by {@link #getAllModels(com.intellij.openapi.module.Module)}.<commit_msg>IDEADEV-12691<commit_after>/*
 * Copyright 2000-2006 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.util.xml.model;

import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.xml.XmlFile;
import com.intellij.util.xml.DomElement;
import com.intellij.util.xml.DomFileElement;
import com.intellij.util.xml.DomManager;
import com.intellij.util.xml.ModelMerger;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;

/**
 * @author <NAME>
 */
public abstract class DomModelFactory<T extends DomElement, M extends DomModel<T>, C extends PsiElement> extends SimpleDomModelFactory<T> {

  private final DomModelCache<M, XmlFile> myModelCache;
  private final DomModelCache<M, Module> myCombinedModelCache;
  private final DomModelCache<List<M>, Module> myAllModelsCache;


  protected DomModelFactory(@NotNull Class<T> aClass,
                          @NotNull ModelMerger modelMerger,
                          final Project project,
                          @NonNls String name) {
    super(aClass, modelMerger);

    myModelCache = new DomModelCache<M, XmlFile>(project, name + " model") {
       @NotNull
       protected CachedValueProvider.Result<M> computeValue(@NotNull XmlFile file) {
         final PsiFile originalFile = file.getOriginalFile();
         if (originalFile != null) {
           file = (XmlFile)originalFile;
         }

         final Module module = ModuleUtil.findModuleForPsiElement(file);
         final M model = module != null ? computeModel(file, module) : null;
         return new CachedValueProvider.Result<M>(model, computeDependencies(file, module));
      }
    };

    myCombinedModelCache = new DomModelCache<M, Module>(project, name + " combined model") {
      @NotNull
      protected CachedValueProvider.Result<M> computeValue(@NotNull final Module module) {
        final M combinedModel = computeCombinedModel(module);
        return new CachedValueProvider.Result<M>(combinedModel, combinedModel == null ? computeDependencies(module) : computeDependencies(combinedModel, module));
      }
    };

    myAllModelsCache = new DomModelCache<List<M>, Module>(project, name + " models list") {
      @NotNull
      protected CachedValueProvider.Result<List<M>> computeValue(@NotNull final Module module) {
        final List<M> models = computeAllModels(module);
        return new CachedValueProvider.Result<List<M>>(models, computeDependencies(module));
      }
    };
  }

  @NotNull
  protected Object[] computeDependencies(@NotNull XmlFile file, @Nullable Module module) {
    if (module == null) {
      return new Object[] {file};
    } else {
      final Object[] moduleDependencies = computeDependencies(module);
      return ArrayUtil.append(moduleDependencies, file);
    }
  }


  @NotNull
  protected Object[] computeDependencies(@NotNull Module module) {
    final ProjectRootManager manager = ProjectRootManager.getInstance(module.getProject());
    return new Object[] { manager };
  }

  @NotNull
  public Object[] computeDependencies(@NotNull M model, @NotNull Module module) {

    final Object[] moduleDependencies = computeDependencies(module);
    final Set<XmlFile> files = model.getConfigFiles();
    final ArrayList<Object> dependencies = new ArrayList<Object>(moduleDependencies.length + files.size());
    dependencies.addAll(files);
    Collections.addAll(dependencies, moduleDependencies);
    return dependencies.toArray(new Object[dependencies.size()]);
  }

  @Nullable
  public abstract M getModel(@NotNull C context);

  @NotNull
  public List<M> getAllModels(@NotNull Module module) {

    final List<M> models = myAllModelsCache.getCachedValue(module);
    if (models == null) {
      return Collections.emptyList();
    }
    else {
      return models;
    }
  }

  @Nullable
  protected abstract List<M> computeAllModels(@NotNull Module module);

  @Nullable
  public M getModelByConfigFile(@Nullable XmlFile psiFile) {
    if (psiFile == null) {
      return null;
    }
    return myModelCache.getCachedValue(psiFile);
  }

  @Nullable
  protected M computeModel(@NotNull XmlFile psiFile, @Nullable Module module) {
    if (module == null) {
      return null;
    }
    final List<M> models = getAllModels(module);
    for (M model: models) {
      final Set<XmlFile> configFiles = model.getConfigFiles();
      if (configFiles.contains(psiFile)) {
        return model;
      }
    }
    return null;
  }

  @Nullable
  public M getCombinedModel(@Nullable Module module) {
    if (module == null) {
      return null;
    }
    return myCombinedModelCache.getCachedValue(module);
  }

  @Nullable
  protected M computeCombinedModel(@NotNull Module module) {
    final List<M> models = getAllModels(module);
    switch (models.size()) {
      case 0:
        return null;
      case 1:
        return models.get(0);
    }
    Set<XmlFile> configFiles = new LinkedHashSet<XmlFile>();
    final ArrayList<T> list = new ArrayList<T>(models.size());
    for (M model: models) {
      final Set<XmlFile> files = model.getConfigFiles();
      for (XmlFile file: files) {
        final T t = getDom(file);
        if (t != null) {
          list.add(t);
        }
      }
      configFiles.addAll(files);
    }
    final T mergedModel = getModelMerger().mergeModels(getClazz(), list);
    final M firstModel = models.get(0);
    return createCombinedModel(configFiles, mergedModel, firstModel);
  }

  /**
   * Factory method to create combined model for given module.
   * Used by {@link #computeCombinedModel(com.intellij.openapi.module.Module)}.
   *
   * @param configFiles file set including all files for all models returned by {@link #getAllModels(com.intellij.openapi.module.Module)}.
{'fraction_non_alphanumeric': 0.10285410657355397, 'fraction_numerical': 0.002808562319720662, 'mean_word_length': 3.6179460217315107, 'pattern_counts': {'":': 0, '<': 75, '<?xml version=': 0, '>': 75, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 12}, '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': '3139488', 'n_tokens_mistral': 3767, 'n_tokens_neox': 3654, 'n_words': 988}