public enum Extension {
  JAVA(".java"), XML(".xml"), GRADLE(".gradle"), PRO(".pro"), JSON(".json");

  private String extensionName;

  Extension(String extensionName) {
    this.extensionName = extensionName;
  }

  @Override
  public String toString() {
    return extensionName.toLowerCase();
  }
} public class AttributeContraints {
  public static final String NAME = "android:name";
} public enum ResourceType {
  LAYOUT("layout")
  , MENU("menu")
  , DRAWABLE("drawable");

  private String name;

  ResourceType(String name) {
    this.name = name;
  }

  @Override
  public String toString() {
    return name.toLowerCase();
  }
} public class CommandException extends RuntimeException {
  public static final String ERROR_MESSAGE_PREFIX = "ask: ";

  public static final int INVALID_NO_OPTIONS = 1;
  public static final int FILE_NOT_FOUND = 2;
  public static final int APP_MODULE_NOT_FOUND = 3;
  public static final int WIDGET_NOT_FOUND = 4;
  public static final int INVALID_WIDGET = 5;
  public static final int OPTION_NOT_FOUND = 6;
  public static final int INVALID_MODULE = 7;
  public static final int NOT_FOUND_ASK_JSON = 8;

  private int errCode;

  public CommandException(String message) {
    this(0, message);
  }

  public CommandException(int errCode) {
    this(errCode, null);
  }

  public CommandException(int errCode, String message) {
    super(message);

    this.errCode = errCode;
  }

  @Override
  public String getMessage() {
    if (errCode == INVALID_NO_OPTIONS) {
      return ERROR_MESSAGE_PREFIX + "there is no options";
    } else if (errCode == FILE_NOT_FOUND) {
      return ERROR_MESSAGE_PREFIX + "failed to find project: " + super.getMessage();
    } else if (errCode == APP_MODULE_NOT_FOUND) {
      return ERROR_MESSAGE_PREFIX + "failed to find module: " + super.getMessage();
    } else if (errCode == WIDGET_NOT_FOUND) {
      return ERROR_MESSAGE_PREFIX + "failed to find widget: " + super.getMessage();
    } else if (errCode == INVALID_WIDGET) {
      return ERROR_MESSAGE_PREFIX + "there is no widgets";
    } else if (errCode == OPTION_NOT_FOUND) {
      return ERROR_MESSAGE_PREFIX + "failed to find option: " + super.getMessage();
    } else if (errCode == INVALID_MODULE) {
      return ERROR_MESSAGE_PREFIX + "failed to find module: " + super.getMessage();
    } else if (errCode == NOT_FOUND_ASK_JSON) {
      return ERROR_MESSAGE_PREFIX + "failed to find ask json file";
    }

    return super.getMessage();
  }

  public boolean shudShowHelp() {
    return errCode == INVALID_NO_OPTIONS
        || errCode == INVALID_WIDGET;
  }
} public class AndroidManifest extends InjectionXmlFile {
  private static final String TAG = AndroidManifest.class.getSimpleName();

  public static final String FILE_NAME = "AndroidManifest.xml";

  private Node applicationNode;
  private Node mainActivityNode;

  private String mainActivityName;
  private String packageName;

  public AndroidManifest(String pathname) {
    super(pathname + "/" + FILE_NAME);

    packageName = rootElement.getAttribute(SyntaxConstraints.IDENTIFIER_PACKAGE);

    if (packageName == null) {
      throw new PackageNotFoundException();
    }

    applicationNode = rootElement.getElementsByTagName(ElementConstraints.APPLICATION).item(0);
    mainActivityNode = getMainActivityNode();

    if (mainActivityNode == null) {
      throw new ActivityNotFoundException();
    }

    mainActivityName = FileUtils.getFilenameFromDotPath(((Element) mainActivityNode).getAttribute(AttributeContraints.NAME));
  }

  public void addPermissions(Permission... permissions) {
    if (permissions == null || permissions.length <= 0) {
      return;
    }

    for (Permission permission : permissions) {
      addUsesPermissionNode(permission.toString());
    }
  }

  public String getMainActivityName() {
    return mainActivityName;
  }

  public String getMainActivityNameEx() {
    return mainActivityName + Extension.JAVA;
  }

  public String getPackageName() {
    return packageName;
  }

  public String getApplicationRelativePathname() {
    NamedNodeMap applicationAttributeNodeMap = applicationNode.getAttributes();

    for (int i = 0, size = applicationAttributeNodeMap.getLength(); i < size; i++) {
      Node attributeNode = applicationAttributeNodeMap.item(i);
      if (attributeNode.getNodeName().equals(AttributeContraints.NAME)) {
        return attributeNode.getNodeValue();
      }
    }
    return null;
  }

  public void addApplicationAttribute(String key, String value) {
    Element applicationElement = (Element) applicationNode;
    applicationElement.setAttribute(key, value);
  }

  public void addMainActivityAttribute(String key, String value) {
    Element activityElement = (Element) mainActivityNode;
    activityElement.setAttribute(key, value);
  }

  private void addUsesPermissionNode(String value) {
    if (!getUsesPermissionNames().contains(value)) {
      Element usesPermissionElement = document.createElement(ElementConstraints.USES_PERMISSION);
      usesPermissionElement.setAttribute(AttributeContraints.NAME, value);
      rootElement.appendChild(usesPermissionElement);
    }
  }

  public Map<String, String> getMainActivityAttributes() {
    NamedNodeMap activityAttributeNodeMap = mainActivityNode.getAttributes();

    Map<String, String> attributes = new HashMap<>();
    if (activityAttributeNodeMap != null) {
      for (int i = 0, size = activityAttributeNodeMap.getLength(); i < size; i++) {
        Node attributeNode = activityAttributeNodeMap.item(i);
        attributes.put(attributeNode.getNodeName(), attributeNode.getNodeValue());
      }
    }

    return attributes;
  }

  @Override
  public void apply() {
    for (ManifestConfig config : configs) {
      for (ManifestElement element : config.getManifestElements()) {
        List<String> nodeNames = element.getNodes();

        Element parentElement = rootElement;
        for (int i = 0, li = nodeNames.size(); i < li; i++) {
          if (i < li - 1) {
            if (!nodeNames.get(i).equals(ElementConstraints.MANIFEST)) {
              parentElement = (Element) parentElement.getElementsByTagName(nodeNames.get(i)).item(0);
            }
          } else {
            Element targetElement = document.createElement(nodeNames.get(i));
            for (String attributeName : element.getAttributes().keySet()) {
              targetElement.setAttribute(attributeName, element.getAttributes().get(attributeName));
            }
            parentElement.appendChild(targetElement);
          }
        }
      }
    }

    super.apply();
  }



  private Node getMainActivityNode() {
    NodeList activityNodes = ((Element) applicationNode).getElementsByTagName(ElementConstraints.ACTIVITY);

    for (int i = 0, li = activityNodes.getLength(); i < li; i++) {
      Node activityNode = activityNodes.item(i);
      if (activityNode.getNodeType() == Node.ELEMENT_NODE) {
        Element activityElement = (Element) activityNode;

        if (activityElement.hasChildNodes()) {
          Node intentFilterNode = activityElement.getElementsByTagName(ElementConstraints.INTENT_FILTER).item(0);

          if (intentFilterNode != null && intentFilterNode.hasChildNodes()) {
            Node actionNode = ((Element) intentFilterNode).getElementsByTagName(ElementConstraints.ACTION).item(0);
            Element actionElement = (Element) actionNode;

            if (actionElement.getAttribute(AttributeContraints.NAME).equals(IntentConstraints.ACTION_MAIN)) {
              return activityElement;
            }
          }
        }
      }
    }

    return null;
  }

  private List<String> getUsesPermissionNames() {
    NodeList usesPermissionNodes = rootElement.getElementsByTagName(ElementConstraints.USES_PERMISSION);

    List<String> usesPermissionNames = new ArrayList<>();

    for (int i = 0, li = usesPermissionNodes.getLength(); i < li; i++) {
      NamedNodeMap attributes = usesPermissionNodes.item(i).getAttributes();
      String nameValue = attributes.getNamedItem(AttributeContraints.NAME).getNodeValue();
      usesPermissionNames.add(nameValue);
    }

    return usesPermissionNames;
  }
} public class BuildGradle extends InjectionGradleFile {
  private static final String TAG = BuildGradle.class.getSimpleName();

  public static final String FILE_NAME = "build.gradle";

  private String applicationId;
  private String supportLibraryVersion;

  public BuildGradle(String modulePath) {
    super(modulePath + "/" + FILE_NAME);

    for (String line : codelines) {
      if (line.contains("applicationId")) {
        applicationId = FileUtils.getStringBetweenQuotes(line);
      } else if (line.contains("com.android.support:appcompat-v7")) {
        String reg = ":([0-9]*\\.[0-9]*\\.[0-9]*)";
        Pattern pattern = Pattern.compile(reg);
        Matcher matcher = pattern.matcher(line);

        if (matcher.find()) {
          supportLibraryVersion = matcher.group(1);
        }
      }
    }

    checkMultipleFlavors();
  }

  public String getApplicationId() {
    return applicationId;
  }

  public String getSupportLibraryVersion() {
    return supportLibraryVersion;
  }

  private void checkMultipleFlavors() {
    List<GradleElement> gradleElements = createGradleElements();

    GradleElement androidGradleElement = getGradleElement(gradleElements, "android");
    if (androidGradleElement != null) {
      GradleElement productFlavorsGradleElement = getGradleElement(
          androidGradleElement.getChildElements(), "productFlavors");

      if (productFlavorsGradleElement != null
          && productFlavorsGradleElement.getChildElements().size() > 0) {
        throw new MultipleFlavorsException();
      }
    }
  }

  private GradleElement getGradleElement(List<GradleElement> childElements, String name) {
    for (GradleElement childGradleElement : childElements) {
      if (childGradleElement.getName().equals(name)) {
        return childGradleElement;
      }
    }

    return null;
  }
} public class MainActivity extends InjectionJavaFile {

  public MainActivity(String fullPathname) {
    super(fullPathname);
  }

  public MainActivity(String pathname, String filename) {
    super(pathname + "/" + filename);
  }
} public class ProguardRules extends InjectionProgardFile {

  public static final String FILE_NAME = "proguard-rules.pro";

  public ProguardRules(String pathName) {
    super(pathName + "/" + FILE_NAME);
  }
} public class ClassDisassembler {
  private String packagePathname;
  private String debugClassesPathname;

  private List<String> classfilterPatterns = Arrays.asList("R", "^R\\$[\\w]+");
  private List<String> externalPackageFilters = Arrays.asList("org/json", "java", "android");

  private List<String> extractedPackagePathnames;

  public ClassDisassembler(String debugClassesPathname, String packagePathname) {
    this.debugClassesPathname = debugClassesPathname;
    this.packagePathname = packagePathname;
  }
  
  public void extractClasses(String classPackagePathname) {
    String shellCommand = "javap -v " + debugClassesPathname + "/" + classPackagePathname + "*.class " +
        "| grep '= Class' " +
        "| grep -v '" + packageFilterToString() + "\\(/[A-Za-z0-9]\\+\\)\\+' " +
        "| awk '{ print $6 }' " +
        "| sort -u";

    extractedPackagePathnames = Arrays.stream(ExecuteShellComand.executeCommand(shellCommand, false).split("\n"))
        .filter(classPathname -> !isExcluded(classPathname) && !classPathname.contains(classPackagePathname))
        .collect(Collectors.toList());
  }

  public List<ClassInfo> getInternalClassInfos() {
    return extractedPackagePathnames.stream()
        .filter(packageFullPathname -> packageFullPathname.contains(packagePathname))
        .map(packageFullPathname -> packageFullPathname.replace(packagePathname + "/", ""))
        .map(ClassInfo::new)
        .collect(Collectors.toList());
  }

  public List<ClassInfo> getExternalClassInfos() {
    return extractedPackagePathnames.stream()
        .filter(packageFullPathname -> !packageFullPathname.contains(packagePathname))
        .map(packageFullPathname -> packageFullPathname.replace(packagePathname + "/", ""))
        .map(ClassInfo::new)
        .collect(Collectors.toList());
  }

  private String packageFilterToString() {
    String filterString = "";
    for (int i = 0, li = externalPackageFilters.size(); i < li; i++) {
      filterString += externalPackageFilters.get(i);

      if (i < li - 1) {
        filterString += "\\|";
      }
    }

    return filterString;
  }

  private boolean isExcluded(String classname) {
    for (String patternString : classfilterPatterns) {
      Pattern pattern = Pattern.compile(packagePathname + "/" + patternString);
      Matcher matcher = pattern.matcher(classname);

      if (matcher.find()) {
        return true;
      }
    }
    return false;
  }
} public class ClassInfo {
  private String name;
  private String packageFullPathname;

  public ClassInfo(String packageFullPathname) {
    this.packageFullPathname = packageFullPathname;
    this.name = FileUtils.getFilenameFromSlashPath(packageFullPathname);
  }

  public String getName() {
    return name;
  }

  public String getNameEx() {
    return name + Extension.JAVA;
  }

  public String getPackageFullPathname() {
    return packageFullPathname;
  }

  @Override
  public String toString() {
    return "ClassInfo{" +
        "name='" + name + '\'' +
        ", packageFullPathname='" + packageFullPathname + '\'' +
        '}';
  }

  @Override
  public boolean equals(Object o) {
    if (o instanceof ClassInfo) {
      ClassInfo classInfo = (ClassInfo) o;
      if (packageFullPathname.equals(classInfo.getPackageFullPathname())) {
        return true;
      }
    }
    return false;
  }
} public class ResourceTransfer implements ResourceExtractable, ResourceTransferable {
  private static final String TAG = ResourceTransfer.class.getSimpleName();

  private SourceDirectory sourceDirectory;

  public ResourceTransfer(SourceDirectory sourceDirectory) {
    this.sourceDirectory = sourceDirectory;
  }

  @Override
  public void extractResourceFileInJava(String input, ResourceMatcher.Handler handler) {
    ResourceMatcher matcher = new ResourceMatcher(input, ResourceMatcher.MatchType.RES_FILE_IN_JAVA);
    matcher.match(handler);
  }

  @Override
  public void extractValuesInJava(String codeLine, ResourceMatcher.Handler handler) {
    ResourceMatcher matcher = new ResourceMatcher(codeLine, ResourceMatcher.MatchType.RES_VALUE_IN_JAVA);
    matcher.match(handler);
  }

  @Override
  public void extractResourceFileInXml(String input, ResourceMatcher.Handler handler) {
    ResourceMatcher matcher = new ResourceMatcher(input, ResourceMatcher.MatchType.RES_FILE_IN_XML);
    matcher.match(handler);
  }

  @Override
  public void extractValuesInXml(String input, ResourceMatcher.Handler handler) {
    ResourceMatcher matcher = new ResourceMatcher(input, ResourceMatcher.MatchType.RES_VALUE_IN_XML);
    matcher.match(handler);
  }

  @Override
  public void transferResourceFileFromRemote(RemoteDirectory remoteDirectory, String resourceTypeName, String layoutName
      , int depth) throws FileNotFoundException {
    File moduleXmlFile = remoteDirectory.getChildFile(layoutName, Extension.XML);

    String codelines = "";
    Scanner scanner = new Scanner(moduleXmlFile);

    System.out.println(PrintUtils.prefixDash(depth) + resourceTypeName + "/" + moduleXmlFile.getName());

    while (scanner.hasNext()) {
      String xmlCodeline = scanner.nextLine()
          .replace(remoteDirectory.getApplicationId(), sourceDirectory.getApplicationId())
          .replace(remoteDirectory.getMainActivityName(), sourceDirectory.getMainActivityName());

      extractResourceFileInXml(xmlCodeline, (resourceTypeName1, layoutName1) -> {
        try {
          transferResourceFileFromRemote(remoteDirectory, resourceTypeName1, layoutName1, depth + 1);
        } catch (FileNotFoundException e) {
          e.printStackTrace();
        }
      });

      extractValuesInXml(xmlCodeline, (resourceTypeName12, elementName) -> {
        try {
          transferValueFromRemote(remoteDirectory, resourceTypeName12, elementName, depth + 1);
        } catch (FileNotFoundException e) {
          e.printStackTrace();
        }
      });

      codelines += xmlCodeline + "\n";

      addDependencyToBuildGradle(xmlCodeline);
    }

    FileUtils.writeFile(sourceDirectory.getResPath() + "/" + resourceTypeName
        , moduleXmlFile.getName()
        , codelines);
  }

  public void transferValueFromRemote(RemoteDirectory remoteDirectory, String resourceTypeName
      , String valueName, int depth) throws FileNotFoundException {
    List<File> valueFiles = remoteDirectory.getChildFiles(resourceTypeName + "s", Extension.XML);
    if (valueFiles == null) {
      throw new FileNotFoundException();
    }

    for (File moduleValueFile : valueFiles) {
      String relativeValueFolderPath = moduleValueFile.getPath().replace(moduleValueFile.getName(), "");
      relativeValueFolderPath = relativeValueFolderPath.substring(relativeValueFolderPath.indexOf("values")
          , relativeValueFolderPath.length() - 1);

      Scanner scanner = new Scanner(moduleValueFile);

      String elementLines = "";
      boolean isCopying = false;

      // Get line from module xml file
      while (scanner.hasNext()) {
        String xmlCodeLine = scanner.nextLine();

        if (xmlCodeLine.contains(valueName) || isCopying) {
          elementLines += xmlCodeLine + SyntaxConstraints.NEWLINE;

          if (SyntaxUtils.hasEndElement(xmlCodeLine, resourceTypeName)) {
            break;
          } else {
            isCopying = true;
          }
        }
      }

      if (StringUtils.isValid(elementLines)) {
        String codeLines = "";
        String sampleValuePathname = FileUtils.linkPathWithSlash(sourceDirectory.getResPath()
            , relativeValueFolderPath
            , moduleValueFile.getName());

        File sampleValueFile = new File(FileUtils.linkPathWithSlash(sampleValuePathname));

        if (!sampleValueFile.exists()) {
          sampleValueFile = createResourceFile(sampleValuePathname);

          System.out.println(PrintUtils.prefixDash(depth) + resourceTypeName + "/" + sampleValueFile.getName());
        }

        scanner = new Scanner(sampleValueFile);
        boolean isDuplicatedElement = false;

        while (scanner.hasNext()) {
          String xmlCodeLine = scanner.nextLine();

          // Check if it has already element
          if (isDuplicatedElement || xmlCodeLine.contains(valueName)) {
            isDuplicatedElement = !SyntaxUtils.hasEndElement(xmlCodeLine, resourceTypeName);
            continue;
          }

          // Check if it is end of element
          if (SyntaxUtils.hasEndElement(xmlCodeLine, ElementConstraints.RESOURCE)
              && !codeLines.contains(elementLines)) {
            codeLines += elementLines;
          }

          codeLines += xmlCodeLine + SyntaxConstraints.NEWLINE;
        }

        FileUtils.writeFile(sampleValueFile, codeLines);
      }
    }
  }

  private void addDependencyToBuildGradle(String codeLine) {
    for (String dependencyKey : sourceDirectory.getExternalLibrary().getKeys()) {
      if (codeLine.contains(dependencyKey)) {
        sourceDirectory.getAppBuildGradleFile()
            .addDependency(sourceDirectory.getExternalLibrary().getInfo(dependencyKey).getLibrary());
        break;
      }
    }
  }

  private File createResourceFile(String pathName) {
    String codelines = SyntaxUtils.createStartElement(ElementConstraints.RESOURCE) + SyntaxConstraints.NEWLINE;
    codelines += SyntaxUtils.createEndElement(ElementConstraints.RESOURCE) + SyntaxConstraints.NEWLINE;

    File file = new File(pathName);
    FileUtils.writeFile(file, codelines);
    return file;
  }
} public class FileUtils {

  public static String getRootPath() {
    File projectDir = new File(".");

    try {
      int index = projectDir.getCanonicalPath().indexOf(AskConfig.DEFAULT_ASK_APP_NAME);

      if (index > 0) {
        return projectDir.getCanonicalPath().substring(0, index - 1);
      }
      return projectDir.getCanonicalPath();
    } catch (IOException e) {
      e.printStackTrace();
    }
    return null;
  }

  public static String linkPathWithSlash(String... args) {
    String path = "";
    for (int i = 0, li = args.length; i < li; i++) {
      path += args[i];

      if (i != li - 1) {
        path += "/";
      }
    }
    return path;
  }

  public static void writeFile(File file, List<String> lineList) {
    writeFile(file, toString(lineList));
  }

  public static void writeFile(File file, String content) {
    if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) {
      return;
    }

    FileWriter fileWriter;
    try {
      fileWriter = new FileWriter(file);
      BufferedWriter bw = new BufferedWriter(fileWriter);
      bw.write(content);
      bw.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  public static void writeFile(String pathName, String fileName, String content) {
    File destDir = new File(pathName);
    if (!destDir.exists() && !destDir.mkdirs()) {
      return;
    }

    writeFile(FileUtils.linkPathWithSlash(pathName, fileName), content);
  }

  public static void writeFile(String filePath, String content) {
    File file = new File(filePath);

    try {
      if (!file.exists()) {
        file.createNewFile();
      }

      writeFile(file, content);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  public static List<String> readFileAsString(File file) {
    Scanner scanner;
    List<String> stringList = new ArrayList<>();

    try {
      scanner = new Scanner(file);

      while (scanner.hasNextLine()) {
        String e = scanner.nextLine();
        stringList.add(e);
      }
    } catch (FileNotFoundException e) {
      throw new RuntimeException("Failed to find : " + file.getPath());
    }

    return stringList;
  }

  public static void copyFile(File moduleFile,
                              String sourceFilePath) throws IOException {
    File destDir = new File(sourceFilePath);
    if (!destDir.exists() && !destDir.mkdirs()) {
      return;
    }

    File sourceFile = moduleFile;
    File destFile = new File(linkPathWithSlash(sourceFilePath, moduleFile.getName()));

    if (!sourceFile.exists()) {
      return;
    }

    if (!destFile.exists()) {
      destFile.createNewFile();
    }

    FileChannel sourceChannel;
    FileChannel destinationChannel;

    sourceChannel = new FileInputStream(sourceFile).getChannel();
    destinationChannel = new FileOutputStream(destFile).getChannel();

    if (destinationChannel != null && sourceChannel != null) {
      destinationChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
    }

    if (sourceChannel != null) {
      sourceChannel.close();
    }

    if (destinationChannel != null) {
      destinationChannel.close();
    }
  }

  public static void copyDirectory(File src, File dest) throws IOException{
    if (src.isDirectory()) {
      if (!dest.exists()) {
        dest.mkdirs();
      }

      String files[] = src.list();

      for (String file : files) {
        File srcFile = new File(src, file);
        File destFile = new File(dest, file);
        copyDirectory(srcFile,destFile);
      }

    } else {
      InputStream in = new FileInputStream(src);
      OutputStream out = new FileOutputStream(dest);

      byte[] buffer = new byte[1024];

      int length;
      while ((length = in.read(buffer)) > 0) {
        out.write(buffer, 0, length);
      }

      in.close();
      out.close();
    }
  }

  public static String changeDotToSlash(String str) {
    return str.replaceAll("\\.", "/");
  }

  public static String getStringBetweenQuotes(String str) {
    Pattern pattern = Pattern.compile("\"([^\"]*)\"");
    Matcher matcher = pattern.matcher(str);
    while (matcher.find()) {
      return matcher.group(1);
    }
    return null;
  }

  public static String getFilenameFromDotPath(String pathname) {
    int index = pathname.lastIndexOf(".");
    if (index < 0) {
      index = 0;
    } else {
      index += 1;
    }
    return pathname.substring(index, pathname.length());
  }

  public static String getFilenameFromSlashPath(String pathname) {
    int index = pathname.lastIndexOf("/");
    if (index < 0) {
      index = 0;
    } else {
      index += 1;
    }
    return pathname.substring(index, pathname.length());
  }

  public static String removeExtension(String filename) {
    return filename.substring(0, filename.lastIndexOf('.'));
  }

  public static String removeFirstSlash(String path) {
    if (path.length() > 0 && path.charAt(0) == '/') {
      path = path.substring(1);
    }

    return path;
  }

  public static String extractIndentInLine(String line) {
    String intent = "";
    for (int i = 0, li = line.length(); i < li; i++) {
      if (line.toCharArray()[i] == ' ') {
        intent += " ";
      } else {
        return intent;
      }
    }
    return intent;
  }

  public static String toString(List<String> strList) {
    final String EoL = System.getProperty("line.separator");

    StringBuilder sb = new StringBuilder();
    for (String line : strList) {
      sb.append(line).append(EoL);
    }

    return sb.toString();
  }

  public static String getStringWithRemovedComment(String line) {
    return line.replaceAll("//.*", "");
  }

  public static int getOpenCurlyBracketCount(String line) {
    int count = 0;
    for ( int i = 0; i < line.length(); i++ ) {
      if (line.charAt(i) == '{') {
        count++;
      }
    }
    return count;
  }

  public static int getCloseCurlyBracketCount(String line) {
    int count = 0;
    for ( int i = 0; i < line.length(); i++ ) {
      if (line.charAt(i) == '}') {
        count++;
      }
    }
    return count;
  }
} public class PrintUtils {
  public static String prefixDash(int depth) {
    String dash;

    if (depth <= 0) {
      dash = "├─ ";
    } else {
      dash = "│";
    }

    for (int i = 0; i < depth; i++) {
      dash += "  ";
    }

    if (depth > 0) {
      dash += "└─ ";
    }

    return dash + (depth > 0 ? " " : "");
  }
}
import com.androidstarterkit.android.api.Extension;
import com.androidstarterkit.android.api.resource.AttributeContraints;
import com.androidstarterkit.android.api.resource.ResourceType;
import com.androidstarterkit.exception.CommandException;
import com.androidstarterkit.file.AndroidManifest;
import com.androidstarterkit.file.BuildGradle;
import com.androidstarterkit.file.MainActivity;
import com.androidstarterkit.file.ProguardRules;
import com.androidstarterkit.tool.ClassDisassembler;
import com.androidstarterkit.tool.ClassInfo;
import com.androidstarterkit.tool.ResourceTransfer;
import com.androidstarterkit.util.FileUtils;
import com.androidstarterkit.util.PrintUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
package com.androidstarterkit.directory;



public class SourceDirectory extends Directory {
  private RemoteDirectory remoteDirectory;

  private String projectPathname;
  private String mainPath;
  private String javaPath;
  private String resPath;
  private String layoutPath;

  private ResourceTransfer resourceTransfer;

  private List<ClassInfo> transformedClassInfos;

  public SourceDirectory(String projectPathname, String sourceModuleName, RemoteDirectory remoteDirectory) {
    super(projectPathname + "/" + sourceModuleName
        , new String[]{"java", "gradle", "xml"}
        , new String[]{"build", "libs", "test", "androidTest", "res"});
    this.projectPathname = projectPathname;
    this.remoteDirectory = remoteDirectory;

    transformedClassInfos = new ArrayList<>();

    resourceTransfer = new ResourceTransfer(this);

    mainPath = FileUtils.linkPathWithSlash(getPath(), "src/main");
    javaPath = FileUtils.linkPathWithSlash(mainPath, "java", applicationId.replaceAll("\\.", "/"));
    resPath = FileUtils.linkPathWithSlash(mainPath, "res");
    layoutPath = FileUtils.linkPathWithSlash(resPath, ResourceType.LAYOUT.toString());
  }

  public BuildGradle getProjectBuildGradle() {
    return new BuildGradle(projectPathname);
  }

  public ProguardRules getProguardRules() {
    return new ProguardRules(getPath());
  }

  public MainActivity getMainActivity() {
    return new MainActivity(getChildDirPath(getMainActivityExtName()), getMainActivityExtName());
  }

  public String getMainPath() {
    return mainPath;
  }

  public String getJavaPath() {
    return javaPath;
  }

  public String getResPath() {
    return resPath;
  }

  public String getLayoutPath() {
    return layoutPath;
  }

  public void transformAndroidManifest() {
    AndroidManifest remoteAndroidManifestFile = remoteDirectory.getAndroidManifestFile();

    System.out.println(PrintUtils.prefixDash(0) + remoteAndroidManifestFile.getName());