code: /**
   * This file is part of Eclipse Steady.
   *
   * 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.
   *
   * SPDX-License-Identifier: Apache-2.0
   * SPDX-FileCopyrightText: Copyright (c) 2018-2020 SAP SE or an SAP affiliate company and Eclipse Steady contributors
   */
  package org.eclipse.steady.java.monitor.touch;
  
  import java.util.HashSet;
  import java.util.Set;
  
  import org.apache.commons.configuration.ConfigurationException;
  import org.apache.logging.log4j.Logger;
  import org.eclipse.steady.ConstructId;
  import org.eclipse.steady.backend.BackendConnectionException;
  import org.eclipse.steady.backend.BackendConnector;
  import org.eclipse.steady.core.util.CoreConfiguration;
  import org.eclipse.steady.java.JavaClassInit;
  import org.eclipse.steady.java.JavaConstructorId;
  import org.eclipse.steady.java.JavaId;
  import org.eclipse.steady.java.JavaMethodId;
  import org.eclipse.steady.java.monitor.ClassVisitor;
  
  /**
   * <p>ConstructIdUtil class.</p>
   */
  public class ConstructIdUtil {
  
    private static Logger log = null;
  
    private static final Logger getLog() {
      if (ConstructIdUtil.log == null)
        ConstructIdUtil.log = org.apache.logging.log4j.LogManager.getLogger(ConstructIdUtil.class);
      return ConstructIdUtil.log;
    }
  
    private static ConstructIdUtil instance = null;
  
    private Set<ConstructId> appConstructs = null;
  
    private ConstructIdUtil() {
      try {
        final Set<org.eclipse.steady.shared.json.model.ConstructId> app_constructs =
            BackendConnector.getInstance()
                .getAppConstructIds(
                    CoreConfiguration.buildGoalContextFromGlobalConfiguration(),
                    CoreConfiguration.getAppContext());
        this.appConstructs = new HashSet<ConstructId>();
        for (org.eclipse.steady.shared.json.model.ConstructId cid : app_constructs) {
          this.appConstructs.add(JavaId.toCoreType(cid));
        }
      } catch (ConfigurationException e) {
        ConstructIdUtil.getLog().error(e.getMessage(), e);
      } catch (BackendConnectionException e) {
        ConstructIdUtil.getLog().error(e.getMessage(), e);
      }
    }
  
    /**
     * <p>Getter for the field <code>instance</code>.</p>
     *
     * @return a {@link org.eclipse.steady.java.monitor.touch.ConstructIdUtil} object.
     */
    public static synchronized ConstructIdUtil getInstance() {
      if (ConstructIdUtil.instance == null) ConstructIdUtil.instance = new ConstructIdUtil();
      return ConstructIdUtil.instance;
    }
  
    /**
     * Checks whether the given {@link ConstructId} is part of the application under analysis.
     * The check is implemented by looing at the definition context, which should be either
     * class or enum. The reason is that, e.g., static initializers are not considered at
     * the time of the source code analysis, hence, are not part of the collection.
     *
     * @param _jid a {@link org.eclipse.steady.ConstructId} object.
     * @return a boolean.
     */
    public boolean isAppConstruct(ConstructId _jid) {
      boolean is_app_construct = false;
  
      // We only instrument clinit, methods and constructors
      if (ConstructIdUtil.isOfInstrumentableType(_jid)) {
        final ConstructId context = _jid.getDefinitionContext();
        is_app_construct = this.appConstructs != null && this.appConstructs.contains(context);
      } else {
        ConstructIdUtil.getLog()
            .error("Expected <CLINIT>, method or constructor, got [" + _jid.toString() + "]");
      }
  
      return is_app_construct;
    }
  
    /**
     * Returns true if the given {@link ConstructId} is neither part of the application nor a test method, false otherwise.
     *
     * @param _jid a {@link org.eclipse.steady.ConstructId} object.
     * @return a boolean.
     */
    public boolean isLibConstruct(ConstructId _jid) {
      boolean is_lib_construct = false;
  
      // We only instrument clinit, methods and constructors
      if (ConstructIdUtil.isOfInstrumentableType(_jid)) {
        final ConstructId context = _jid.getDefinitionContext();
        is_lib_construct = this.appConstructs != null && !this.appConstructs.contains(context);
  
        // Not part of the app, now check that is not a JUnit test method of the app
        if (is_lib_construct) {
          if (_jid instanceof JavaMethodId) {
            is_lib_construct = !((JavaMethodId) _jid).isTestMethod();
          }
        }
      } else {
        ConstructIdUtil.getLog()
            .error("Expected <CLINIT>, method or constructor, got [" + _jid.toString() + "]");
      }
  
      return is_lib_construct;
    }
  
    /**
     * Returns true if the given {@link JavaId} is an instance of {@link JavaClassInit}, {@link JavaMethodId} or
     * {@link JavaConstructorId}, false otherwise.
     *
     * @param _jid a {@link org.eclipse.steady.ConstructId} object.
     * @return a boolean.
     */
    public static boolean isOfInstrumentableType(ConstructId _jid) {
      return _jid instanceof JavaClassInit
          || _jid instanceof JavaConstructorId
          || _jid instanceof JavaMethodId;
    }
  
    /**
     * Given a qualified name return the ConstructId that represent it. For now is
     * implemented only on constructors and method (also including &lt;clinit&gt; and &lt;init&gt;).
     * If the type requested is null or is not in the range of teh allowed types the method return null
     *
     * @param _qname the qname of the construct.
     * @param type can be CONSTRUCTOR,CLASSINIT,METHOD,CLASS,NESTED_CLASS
     * @return Return the java representation of this constructid or null if is not found
     */
    public ConstructId getConstructidFromQName(String _qname, String type) {
      if (_qname == null || type == null) return null;
      // Constructor
      if (_qname.contains("<init>") || type.contains("CONSTRUCTOR")) {
        _qname = ClassVisitor.removeParameterQualification(_qname);
        return JavaId.parseConstructorQName(_qname);
      }
      // Static initializer
      else if (_qname.contains("<clinit>") || type.contains("CLASSINIT")) {
        return JavaId.parseClassInitQName(_qname.substring(0, _qname.lastIndexOf('>') + 1));
      }
      // Method
      else if (type.contains("METHOD")) {
        // Cleanup the method's string representation for parsing
        _qname = ClassVisitor.removeParameterQualification(_qname);
        return JavaId.parseMethodQName(_qname);
      } else if (type.contains("CLASS") || type.contains("NESTED_CLASS")) {
        return JavaId.parseClassQName(_qname);
      } else if (type.contains("ENUM")) {
        return JavaId.parseEnumQName(_qname);
      } else return null;
    }
  }
  