content: // 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 com.cloud.hypervisor;
  
  import com.cloud.storage.Storage.ImageFormat;
  import org.apache.commons.lang3.StringUtils;
  
  import java.util.LinkedHashMap;
  import java.util.List;
  import java.util.Locale;
  import java.util.Map;
  import java.util.Objects;
  import java.util.Set;
  import java.util.EnumSet;
  import java.util.stream.Collectors;
  
  import static com.cloud.hypervisor.Hypervisor.HypervisorType.Functionality.DirectDownloadTemplate;
  import static com.cloud.hypervisor.Hypervisor.HypervisorType.Functionality.RootDiskSizeOverride;
  import static com.cloud.hypervisor.Hypervisor.HypervisorType.Functionality.VmStorageMigration;
  import static com.cloud.hypervisor.Hypervisor.HypervisorType.Functionality.VmStorageMigrationWithSnapshots;
  
  public class Hypervisor {
      public static class HypervisorType {
          public enum Functionality {
              DirectDownloadTemplate,
              RootDiskSizeOverride,
              VmStorageMigration,
              VmStorageMigrationWithSnapshots
          }
  
          private static final Map<String, HypervisorType> hypervisorTypeMap = new LinkedHashMap<>();
          public static final HypervisorType None = new HypervisorType("None"); //for storage hosts
          public static final HypervisorType XenServer = new HypervisorType("XenServer", ImageFormat.VHD, EnumSet.of(RootDiskSizeOverride, VmStorageMigration));
          public static final HypervisorType KVM = new HypervisorType("KVM", ImageFormat.QCOW2, EnumSet.of(DirectDownloadTemplate, RootDiskSizeOverride, VmStorageMigration));
          public static final HypervisorType VMware = new HypervisorType("VMware", ImageFormat.OVA, EnumSet.of(RootDiskSizeOverride, VmStorageMigration, VmStorageMigrationWithSnapshots));
          public static final HypervisorType Hyperv = new HypervisorType("Hyperv");
          public static final HypervisorType VirtualBox = new HypervisorType("VirtualBox");
          public static final HypervisorType Parralels = new HypervisorType("Parralels");
          public static final HypervisorType BareMetal = new HypervisorType("BareMetal");
          public static final HypervisorType Simulator = new HypervisorType("Simulator", null, EnumSet.of(RootDiskSizeOverride, VmStorageMigration));
          public static final HypervisorType Ovm = new HypervisorType("Ovm", ImageFormat.RAW);
          public static final HypervisorType Ovm3 = new HypervisorType("Ovm3", ImageFormat.RAW);
          public static final HypervisorType LXC = new HypervisorType("LXC");
          public static final HypervisorType Custom = new HypervisorType("Custom", null, EnumSet.of(RootDiskSizeOverride));
          public static final HypervisorType External = new HypervisorType("External", null, EnumSet.of(RootDiskSizeOverride));
          public static final HypervisorType Any = new HypervisorType("Any"); /*If you don't care about the hypervisor type*/
          private final String name;
          private final ImageFormat imageFormat;
          private final Set<Functionality> supportedFunctionalities;
  
          public HypervisorType(String name) {
              this(name, null, EnumSet.noneOf(Functionality.class));
          }
  
          public HypervisorType(String name, ImageFormat imageFormat) {
              this(name, imageFormat, EnumSet.noneOf(Functionality.class));
          }
  
          public HypervisorType(String name, ImageFormat imageFormat, Set<Functionality> supportedFunctionalities) {
              this.name = name;
              this.imageFormat = imageFormat;
              this.supportedFunctionalities = supportedFunctionalities;
              if (name.equals("Parralels")){ // typo in the original code
                  hypervisorTypeMap.put("parallels", this);
              } else {
                  hypervisorTypeMap.putIfAbsent(name.toLowerCase(Locale.ROOT), this);
              }
          }
  
          public static HypervisorType getType(String hypervisor) {
              return hypervisor == null ? HypervisorType.None :
                      (hypervisor.toLowerCase(Locale.ROOT).equalsIgnoreCase(
                              HypervisorGuru.HypervisorCustomDisplayName.value()) ? Custom :
                              hypervisorTypeMap.getOrDefault(hypervisor.toLowerCase(Locale.ROOT), HypervisorType.None));
          }
  
          public static HypervisorType[] values() {
              return hypervisorTypeMap.values().toArray(HypervisorType[]::new).clone();
          }
  
          public static HypervisorType valueOf(String name) {
              if (StringUtils.isBlank(name)) {
                  return null;
              }
  
              HypervisorType hypervisorType = hypervisorTypeMap.get(name.toLowerCase(Locale.ROOT));
              if (hypervisorType == null) {
                  throw new IllegalArgumentException("HypervisorType '" + name + "' not found");
              }
              return hypervisorType;
          }
  
          public static List<HypervisorType> getListOfHypervisorsSupportingFunctionality(Functionality functionality) {
              return hypervisorTypeMap.values().stream()
                      .filter(hypervisor -> hypervisor.supportedFunctionalities.contains(functionality))
                      .collect(Collectors.toList());
          }
  
          /**
           * Returns the display name of a hypervisor type in case the custom hypervisor is used,
           * using the 'hypervisor.custom.display.name' setting. Otherwise, returns hypervisor name
           */
          public String getHypervisorDisplayName() {
              return HypervisorType.Custom.equals(this) ? HypervisorGuru.HypervisorCustomDisplayName.value() : name;
          }
  
          /**
           * This method really needs to be part of the properties of the hypervisor type itself.
           *
           * @return
           */
          public ImageFormat getSupportedImageFormat() {
              return imageFormat;
          }
  
          public String name() {
              return name;
          }
  
          /**
           * Make this method to be part of the properties of the hypervisor type itself.
           *
           * @return true if the hypervisor plugin support the specified functionality
           */
          public boolean isFunctionalitySupported(Functionality functionality) {
              return supportedFunctionalities.contains(functionality);
          }
  
          @Override
          public int hashCode() {
              return Objects.hash(name);
          }
  
          @Override
          public boolean equals(Object o) {
              if (o == this) {
                  return true;
              } else if (o == null || getClass() != o.getClass()) {
                  return false;
              }
              HypervisorType that = (HypervisorType) o;
              return Objects.equals(name, that.name);
          }
  
          @Override
          public String toString() {
              return name;
          }
      }
  
  }
  