package jadx.core.deobf;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.List;

public class PackageNode {

	private static final char SEPARATOR_CHAR = '.';

	private PackageNode parentPackage;
	private List<PackageNode> innerPackages = Collections.emptyList();

	private final String packageName;
	private String packageAlias;

	private String cachedPackageFullName;
	private String cachedPackageFullAlias;

	public PackageNode(String packageName) {
		this.packageName = packageName;
		this.parentPackage = this;
	}

	public String getName() {
		return packageName;
	}

	public String getFullName() {
		if (cachedPackageFullName == null) {
			Deque<PackageNode> pp = getParentPackages();

			StringBuilder result = new StringBuilder();
			result.append(pp.pop().getName());
			while (!pp.isEmpty()) {
				result.append(SEPARATOR_CHAR);
				result.append(pp.pop().getName());
			}
			cachedPackageFullName = result.toString();
		}
		return cachedPackageFullName;
	}

	public String getAlias() {
		if (packageAlias != null) {
			return packageAlias;
		}
		return packageName;
	}

	public void setAlias(String alias) {
		packageAlias = alias;
	}

	public boolean hasAlias() {
		return packageAlias != null;
	}

	public boolean hasAnyAlias() {
		if (hasAlias()) {
			return true;
		}
		if (parentPackage != this) {
			return parentPackage.hasAnyAlias();
		}
		return false;
	}

	public String getFullAlias() {
		if (cachedPackageFullAlias == null) {
			Deque<PackageNode> pp = getParentPackages();
			StringBuilder result = new StringBuilder();

			if (!pp.isEmpty()) {
				result.append(pp.pop().getAlias());
				while (!pp.isEmpty()) {
					result.append(SEPARATOR_CHAR);
					result.append(pp.pop().getAlias());
				}
			} else {
				result.append(this.getAlias());
			}
			cachedPackageFullAlias = result.toString();
		}
		return cachedPackageFullAlias;
	}

	public PackageNode getParentPackage() {
		return parentPackage;
	}

	public List<PackageNode> getInnerPackages() {
		return innerPackages;
	}

	public void addInnerPackage(PackageNode pkg) {
		if (innerPackages.isEmpty()) {
			innerPackages = new ArrayList<>();
		}
		innerPackages.add(pkg);
		pkg.parentPackage = this;
	}

	/**
	 * Gets inner package node by name
	 *
	 * @param name inner package name
	 * @return package node or {@code null}
	 */
	public PackageNode getInnerPackageByName(String name) {
		PackageNode result = null;
		for (PackageNode p : innerPackages) {
			if (p.getName().equals(name)) {
				result = p;
				break;
			}
		}
		return result;
	}

	/**
	 * Fills stack with parent packages exclude root node
	 *
	 * @return stack with parent packages
	 */
	private Deque<PackageNode> getParentPackages() {
		Deque<PackageNode> pp = new ArrayDeque<>();

		PackageNode currentPkg = this;
		PackageNode parentPkg = currentPkg.getParentPackage();
		while (currentPkg != parentPkg) {
			pp.push(currentPkg);
			currentPkg = parentPkg;
			parentPkg = currentPkg.getParentPackage();
		}
		return pp;
	}

	@Override
	public String toString() {
		return packageAlias;
	}
}

@@ -763,17 +763,84 @@
 ages();%0A
-%0A
+%09%09%09if (pp.isEmpty()) %7B%0A%09%09%09%09cachedPackageFullName = %22%22;%0A%09%09%09%7D else %7B%0A%09
 %09%09%09Strin
@@ -881,16 +881,17 @@
 r();%0A%09%09%09
+%09
 result.a
@@ -917,16 +917,17 @@
 ame());%0A
+%09
 %09%09%09while
@@ -941,32 +941,33 @@
 sEmpty()) %7B%0A%09%09%09%09
+%09
 result.append(SE
@@ -977,24 +977,25 @@
 ATOR_CHAR);%0A
+%09
 %09%09%09%09result.a
@@ -1017,32 +1017,33 @@
 getName());%0A
+%09
 %09%09%09%7D%0A%09%09%09
 cachedPackag
@@ -1022,32 +1022,33 @@
 me());%0A%09%09%09%09%7D%0A%09%09%09
+%09
 cachedPackageFul
@@ -1066,32 +1066,37 @@
 ult.toString();%0A
+%09%09%09%7D%0A
 %09%09%7D%0A%09%09return cac

resolve NPE when package is empty (if rename is disabled)