All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.jangaroo.jooc.mvnplugin.sencha.SenchaUtils Maven / Gradle / Ivy

There is a newer version: 4.1.17
Show newest version
package net.jangaroo.jooc.mvnplugin.sencha;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMap;
import net.jangaroo.jooc.mvnplugin.Type;
import net.jangaroo.jooc.mvnplugin.sencha.executor.SenchaCmdExecutor;
import net.jangaroo.jooc.mvnplugin.util.MavenDependencyHelper;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;

public class SenchaUtils {

  public static final String SEPARATOR = "/";

  public static final String LOCAL_PACKAGES_PATH = "/packages/";

  private static final String BUILD_PATH = "build/";

  public static final String APP_TARGET_DIRECTORY = "/app";

  /**
   * The name of the folder of the generated module inside the packages folder of the module.
   * Make sure that the name is not too long to avoid exceeding the max path length in windows.
   * The old path length relative to the target folder was 43 chars:
   * classes\META-INF\resources\joo\classes\com
   *
   * So to avoid compatiblity issues the max length for the path is:
   *
   * 43 - SENCHA_BASE_BATH.length - SENCHA_PACKAGES.length - SENCHA_PACKAGE_LOCAL.length
   *    - SENCHA_CLASS_PATH.length - 4 (Separator)
   */
  public static final String SENCHA_OVERRIDES_PATH = "overrides";
  public static final String SENCHA_LOCALE_PATH = "locale";
  public static final String SENCHA_RESOURCES_PATH = "resources";
  public static final String SENCHA_BUNDLED_RESOURCES_PATH = "bundledResources";
  public static final String PRODUCTION_PROFILE = "production";
  public static final String TESTING_PROFILE = "testing";
  public static final String DEVELOPMENT_PROFILE = "development";

  public static final String TOOLKIT_CLASSIC = "classic";


  private static final String SENCHA_CFG = "sencha.cfg";
  public static final String SENCHA_DIRECTORYNAME = ".sencha";
  public static final String SENCHA_WORKSPACE_CONFIG = SENCHA_DIRECTORYNAME + SEPARATOR + "workspace" + SEPARATOR + SENCHA_CFG;
  public static final String SENCHA_PACKAGE_CONFIG = SENCHA_DIRECTORYNAME + SEPARATOR + "package" + SEPARATOR + SENCHA_CFG;
  public static final String SENCHA_APP_CONFIG = SENCHA_DIRECTORYNAME + SEPARATOR + "app" + SEPARATOR + SENCHA_CFG;

  public static final String SENCHA_WORKSPACE_FILENAME = "workspace.json";
  public static final String SENCHA_PACKAGE_FILENAME = "package.json";
  public static final String SENCHA_APP_FILENAME = "app.json";
  public static final String SENCHA_PKG_EXTENSION = ".pkg";
  public static final String PACKAGE_CONFIG_FILENAME = "packageConfig.js";
  public static final String REQUIRED_CLASSES_FILENAME = "requiredClasses.js";

  public static final String SENCHA_TEST_APP_TEMPLATE_ARTIFACT_ID = "sencha-test-app-template";
  public static final String SENCHA_APP_TEMPLATE_ARTIFACT_ID = "sencha-app-template";
  public static final String SENCHA_APP_TEMPLATE_GROUP_ID = "net.jangaroo";

  private static final Pattern SENCHA_VERSION_PATTERN = Pattern.compile("^[0-9]+(\\.[0-9]+){0,3}$");

  public static final String AUTO_CONTENT_COMMENT = "DO NOT CHANGE - This file was automatically generated by the " +
          "jangaroo-maven-plugin and it will be overwritten by the next call of the plugin's " +
          "goal \"generate-workspace\".";

  public static final Map PLACEHOLDERS = ImmutableMap.of( // TODO data structure and location??
          Type.APP, "${app.dir}",
          Type.CODE, "${package.dir}",
          Type.THEME, "${package.dir}",
          Type.WORKSPACE, "${workspace.dir}"
  );

  private static final ObjectMapper objectMapper;
  static {
    objectMapper = new ObjectMapper();
    objectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
  }

  private SenchaUtils() {
    // hide constructor
  }

  public static String getSenchaPackageName(String groupId, String artifactId) {
    return groupId + "__" + artifactId;
  }

  public static String getSenchaPackageName(@Nonnull MavenProject project) {
    return getSenchaPackageName(project.getGroupId(), project.getArtifactId());
  }

  public static String getSenchaVersionForMavenVersion(String version) {
    // Very simple matching for now, maybe needs some adjustment
    String senchaVersion = version.replaceAll("[^0-9.-]", "").replace("-", ".").replaceAll("[.]+", ".").replaceAll("[.]+$", "");
    if (SENCHA_VERSION_PATTERN.matcher(senchaVersion).matches()) {
      return senchaVersion;
    } else {
      return null;
    }
  }

  @Nullable
  public static Dependency getThemeDependency(@Nullable String theme, @Nonnull MavenProject project) {
    Dependency themeDependency = MavenDependencyHelper.fromKey(theme);
    // verify that provided artifact is under project dependencies
    Set dependencyArtifacts = project.getDependencyArtifacts();
    for (Artifact artifact : dependencyArtifacts) {
      Dependency artifactDependency = MavenDependencyHelper.fromArtifact(artifact);
      if (MavenDependencyHelper.equalsGroupIdAndArtifactId(artifactDependency, themeDependency)) {
        return artifactDependency;
      }
    }
    return null;
  }

  public static File findClosestSenchaWorkspaceDir(File dir) {
    File result;
    try {
      result = dir.getCanonicalFile();
    } catch (IOException e) {
      return null; //NOSONAR
    }
    while (null != result) {
      String[] list = result.list(new FilenameFilter() {
        @Override
        public boolean accept(File dir, String name) {
          return SenchaUtils.SENCHA_WORKSPACE_FILENAME.equals(name);
        }
      });
      if (null != list
              && list.length > 0) {
        break;
      }
      result = result.getParentFile();
    }
    return result;
  }

  /**
   * Generates an absolute path to the module dir for the given relative path using a placeholder.
   *
   * @param packageType the Maven project's packaging type
   * @param relativePath the path relative to the Sencha module
   * @return path prefixed with a placeholder and a separator to have an absolute path
   */
  public static String generateAbsolutePathUsingPlaceholder(String packageType, String relativePath) {
    // make sure only normal slashes are used and no backslashes (e.g. on windows systems)
    String normalizedRelativePath = FilenameUtils.separatorsToUnix(relativePath);
    String result = PLACEHOLDERS.get(packageType);
    if (StringUtils.isNotEmpty(normalizedRelativePath) && !normalizedRelativePath.startsWith(SEPARATOR)) {
      result += SEPARATOR + normalizedRelativePath;
    }
    return result;
  }

  public static ObjectMapper getObjectMapper() {
    return objectMapper;
  }

  public static Path getRelativePathFromWorkspaceToWorkingDir(File workingDirectory) throws MojoExecutionException {
    File closestSenchaWorkspaceDir = findClosestSenchaWorkspaceDir(workingDirectory);
    if (null == closestSenchaWorkspaceDir) {
      throw new MojoExecutionException("could not find Sencha workspace above workingDirectory");
    }
    Path workspacePath = closestSenchaWorkspaceDir.toPath().normalize();
    Path workingDirectoryPath = workingDirectory.toPath().normalize();

    if (workspacePath.getRoot() == null || !workspacePath.getRoot().equals(workingDirectoryPath.getRoot())) {
      throw new MojoExecutionException("cannot find a relative path from workspace directory to working directory");
    }
    return workspacePath.relativize(workingDirectoryPath);
  }

  public static boolean isRequiredSenchaDependency(@Nonnull Dependency dependency,
                                                   @Nonnull Dependency remotePackageDependency) {
    return !MavenDependencyHelper.equalsGroupIdAndArtifactId(dependency, remotePackageDependency)
            && Type.JAR_EXTENSION.equals(dependency.getType())
            && !Artifact.SCOPE_PROVIDED.equals(dependency.getScope())
            && !Artifact.SCOPE_TEST.equals(dependency.getScope());
  }

  public static String generateSenchaAppId(MavenProject project) {
    String appIdString = SenchaUtils.getSenchaPackageName(project) +
            SenchaUtils.getSenchaVersionForMavenVersion(project.getVersion());
    return UUID.nameUUIDFromBytes(appIdString.getBytes()).toString();
  }

  public static boolean doesSenchaAppExist(File directory) {
    File senchaCfg = new File(directory, SenchaUtils.SENCHA_APP_CONFIG);
    return senchaCfg.exists();
  }

  public static String getPackagesPath(MavenProject project) {
    return LOCAL_PACKAGES_PATH + getSenchaPackageName(project);
  }

  public static String getPackagesBuildPath(MavenProject project) {
    return getPackagesPath(project) + "/" + BUILD_PATH;
  }

  public static void generateSenchaWorkspace(File workingDirectory, String extDirectory, Log log, String logLevel)
          throws MojoExecutionException {

    Path senchaCfg = Paths.get(workingDirectory.getAbsolutePath(), SenchaUtils.SENCHA_WORKSPACE_CONFIG);
    try {
      // delete existing senchaCfg so that Sencha Cmd is forced to re-create it
      Files.deleteIfExists(senchaCfg);
    } catch (IOException ioe) {
      throw new MojoExecutionException("Could not delete existing sencha.cfg file in " + senchaCfg, ioe);
    }

    log.info("Generating Sencha workspace module");
    SenchaCmdExecutor senchaCmdExecutor = new SenchaCmdExecutor(workingDirectory, "generate workspace .", log, logLevel);
    senchaCmdExecutor.execute();

    // sencha.cfg should be recreated
    updateSenchaCfgWithExtDirectory(senchaCfg, extDirectory);
  }

    public static void generateSenchaAppFromTemplate(File workingDirectory,
                                                        String appName,
                                                        String applicationClass,
                                                        String toolkit,
                                                        Log log,
                                                        String logLevel
  ) throws MojoExecutionException {
    String templateName = getSenchaPackageName(SENCHA_APP_TEMPLATE_GROUP_ID, SENCHA_APP_TEMPLATE_ARTIFACT_ID) + "/tpl";
    ImmutableMap properties = ImmutableMap.of("appName", appName, "applicationClass", applicationClass);
    generateSenchaAppFromTemplate(workingDirectory, appName, toolkit, templateName, properties, log, logLevel);
  }

  public static void generateSenchaTestAppFromTemplate(File workingDirectory,
                                                       MavenProject project,
                                                       String appName,
                                                       String testSuite,
                                                       String toolkit,
                                                       Log log,
                                                       String logLevel
  ) throws MojoExecutionException {
    String templateName = getSenchaPackageName(SENCHA_APP_TEMPLATE_GROUP_ID, SENCHA_TEST_APP_TEMPLATE_ARTIFACT_ID) + "/tpl";
    ImmutableMap properties = ImmutableMap.of("moduleName", getSenchaPackageName(project), "testSuite", testSuite);
    generateSenchaAppFromTemplate(workingDirectory, appName, toolkit, templateName, properties, log, logLevel);
  }

  public static void generateSenchaAppFromTemplate(File workingDirectory,
                                                   String appName,
                                                   String toolkit,
                                                   String templateName,
                                                   Map properties,
                                                   Log log,
                                                   String logLevel
  ) throws MojoExecutionException {
    StringBuilder arguments = new StringBuilder("generate app")
            .append(" -ext ")
            .append(toolkit)
            .append(" --template ").append(templateName)
            .append(" --path=\"\"")
            .append(" --refresh=false");

    if (properties != null) {
      for (Map.Entry entry: properties.entrySet()) {
        arguments.append(String.format(" -D%s=%s",entry.getKey(), entry.getValue()));
      }
    }
    arguments.append(" ").append(appName);

    SenchaCmdExecutor senchaCmdExecutor = new SenchaCmdExecutor(workingDirectory, arguments.toString(), log, logLevel);
    senchaCmdExecutor.execute();
  }

  public static void refreshApp(File dir, Log log, String logLevel) throws MojoExecutionException {
    SenchaCmdExecutor senchaCmdExecutor = new SenchaCmdExecutor(dir, "app refresh", log, logLevel);
    senchaCmdExecutor.execute();
  }

  private static void updateSenchaCfgWithExtDirectory(Path senchaCfg, String extDirectory) throws MojoExecutionException {
   createSenchaCfgWithExtDirectory(senchaCfg, senchaCfg, extDirectory);
  }

  public static void createSenchaCfgWithExtDirectory(Path senchaCfgSource, Path senchaCfgTarget, String extDirectory)
          throws MojoExecutionException {
    if (Files.exists(senchaCfgSource)) {

      try {
        List senchaCfgTmpContent = Files.readAllLines(senchaCfgSource, Charset.forName("UTF-8"));
        Files.write(senchaCfgTarget, getSenchaCfgContent(senchaCfgTmpContent, extDirectory), Charset.forName("UTF-8"));
      } catch (IOException e) {
        throw new MojoExecutionException("Modifying sencha.cfg file failed", e);
      }

    } else {
      throw new MojoExecutionException("Could not find sencha.cfg file in " + senchaCfgSource);
    }
  }

  private static List getSenchaCfgContent(@Nonnull List currentContent, String extDirectory) {
    // prepend comment and delete first comment line with usually contains the date time
    if (currentContent.get(0).startsWith("#")) { // if the first
      currentContent.remove(0);
    }

    List newSenchaCfg = new ArrayList<>(currentContent.size());
    newSenchaCfg.add("#");
    newSenchaCfg.add("# " + AUTO_CONTENT_COMMENT);
    newSenchaCfg.add("#");
    newSenchaCfg.add("");
    newSenchaCfg.addAll(currentContent);

    newSenchaCfg.add("ext.dir=" + extDirectory);
    return newSenchaCfg;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy