001/* 002 * Copyright (c) 2012-2017 Institut National des Sciences Appliquées de Lyon (INSA-Lyon) 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 */ 009 010package org.eclipse.golo.compiler; 011 012import static java.util.Objects.requireNonNull; 013 014/** 015 * Represents a package and class. 016 */ 017public final class PackageAndClass { 018 019 private final String packageName; 020 private final String className; 021 022 /** 023 * Makes a new package and class definition. 024 * 025 * @param packageName the package name. 026 * @param className the class name. 027 */ 028 public PackageAndClass(String packageName, String className) { 029 this.packageName = requireNonNull(packageName); 030 this.className = requireNonNull(className); 031 if (className.isEmpty()) { 032 throw new IllegalArgumentException("The class name can't be empty"); 033 } 034 } 035 036 /** 037 * Extracts a package and class definition from a string. 038 * 039 * @param qualifiedName a qualified name. 040 * @return a package and class definition. 041 */ 042 public static PackageAndClass fromString(String qualifiedName) { 043 return new PackageAndClass( 044 extractTargetJavaPackage(qualifiedName), 045 extractTargetJavaClass(qualifiedName)); 046 } 047 048 private static int packageClassSeparatorIndex(String moduleName) { 049 if (moduleName != null) { 050 return moduleName.lastIndexOf('.'); 051 } 052 return -1; 053 } 054 055 private static String extractTargetJavaPackage(String moduleName) { 056 int packageClassSeparatorIndex = packageClassSeparatorIndex(moduleName); 057 if (packageClassSeparatorIndex > 0) { 058 return moduleName.substring(0, packageClassSeparatorIndex); 059 } 060 return ""; 061 } 062 063 private static String extractTargetJavaClass(String moduleName) { 064 int packageClassSeparatorIndex = packageClassSeparatorIndex(moduleName); 065 if (packageClassSeparatorIndex > 0) { 066 return moduleName.substring(packageClassSeparatorIndex + 1); 067 } 068 return moduleName; 069 } 070 071 072 /** 073 * Create an inner class. 074 * <p> 075 * For instance: 076 * <pre class="listing"><code class="lang-java" data-lang="java"> 077 * PackageAndClass cls = new PackageAndClass("foo.bar", "Spam"); 078 * PackageAndClass inner = cls.createInnerClass("Egg"); // foo.bar.Spam$Egg 079 * </code></pre> 080 * 081 * @return a new {@code PackageAndClass} identifying an inner class of this class. 082 */ 083 public PackageAndClass createInnerClass(String name) { 084 return new PackageAndClass( 085 this.packageName, 086 this.className + "$" + name.replace('.', '$')); 087 } 088 089 /** 090 * Create a sibling class. 091 * <p> 092 * For instance: 093 * <pre class="listing"><code class="lang-java" data-lang="java"> 094 * PackageAndClass list = new PackageAndClass("java.util", "List"); 095 * PackageAndClass set = list.createSiblingClass("Set"); // java.util.Set 096 * </code></pre> 097 * 098 * @return a new {@code PackageAndClass} identifying an alternate class in the same package. 099 */ 100 public PackageAndClass createSiblingClass(String name) { 101 return new PackageAndClass(this.packageName, name); 102 } 103 104 /** 105 * Create a sub-package. 106 * <p> 107 * For instance: 108 * <pre class="listing"><code class="lang-java" data-lang="java"> 109 * PackageAndClass pc = new PackageAndClass("foo.bar", "Module"); 110 * PackageAndClass sub = pc.createSubPackage("types"); // foo.bar.Modules.types 111 * </code></pre> 112 * 113 * @return a new {@code PackageAndClass} identifying a sub-package of this package. 114 */ 115 public PackageAndClass createSubPackage(String name) { 116 return new PackageAndClass( 117 this.packageName.isEmpty() ? this.className : this.packageName + "." + this.className, name); 118 } 119 120 /** 121 * Create a class in another package. 122 * <p> 123 * For instance: 124 * <pre class="listing"><code class="lang-java" data-lang="java"> 125 * PackageAndClass pc = PackageAndClass.fromString("foo.bar.Baz"); 126 * PackageAndClass other = pc.inPackage("plic.ploc"); // plic.ploc.Baz 127 * </code></pre> 128 * 129 * @return a new {@code PackageAndClass} representing the same class in another package. 130 * @param qualifiedName the qualified name of the new package. 131 */ 132 public PackageAndClass inPackage(String qualifiedName) { 133 return new PackageAndClass(qualifiedName, className); 134 } 135 136 /** 137 * Create a class in the same package as another one. 138 * <p> 139 * For instance: 140 * <pre class="listing"><code class="lang-java" data-lang="java"> 141 * PackageAndClass pc = PackageAndClass.fromString("foo.bar.Baz"); 142 * PackageAndClass other = PackageAndClass.fromString("plic.ploc.Foo"); 143 * PackageAndClass newOne = pc.inPackage(other); // plic.ploc.Baz 144 * </code></pre> 145 * 146 * @return a new {@code PackageAndClass} representing the same class in another package. 147 * @param parent the {@code PackageAndClass} representing the new package. 148 */ 149 public PackageAndClass inPackage(PackageAndClass parent) { 150 return new PackageAndClass(parent.packageName(), className); 151 } 152 153 /** 154 * @return the package name. 155 */ 156 public String packageName() { 157 return packageName; 158 } 159 160 /** 161 * Check if this {@code PackageAndClass} has a package component. 162 */ 163 public boolean hasPackage() { 164 return !packageName.isEmpty(); 165 } 166 167 /** 168 * @return the package as a {@code PackageAndClass} 169 */ 170 public PackageAndClass parentPackage() { 171 return fromString(packageName); 172 } 173 174 /** 175 * @return the class name. 176 */ 177 public String className() { 178 return className; 179 } 180 181 @Override 182 public String toString() { 183 if (packageName.isEmpty()) { 184 return className; 185 } else { 186 return packageName + "." + className; 187 } 188 } 189 190 /** 191 * @return a JVM type representation for this object, e.g.: {@code foo.Bar} gives {@code foo/Bar}. 192 */ 193 public String toJVMType() { 194 return toString().replaceAll("\\.", "/"); 195 } 196 197 /** 198 * @return a JVM reference type representation for this object, e.g.: {@code foo.Bar} gives 199 * {@code Lfoo/Bar;} 200 */ 201 public String toJVMRef() { 202 return "L" + toJVMType() + ";"; 203 } 204 205 /** 206 * @return a mangled named for this class. 207 */ 208 public String mangledName() { 209 return toString().replace('.', '$'); 210 } 211 212 @Override 213 public boolean equals(Object o) { 214 if (this == o) return true; 215 if (o == null || getClass() != o.getClass()) return false; 216 217 PackageAndClass that = (PackageAndClass) o; 218 219 if (className != null ? !className.equals(that.className) : that.className != null) return false; 220 if (packageName != null ? !packageName.equals(that.packageName) : that.packageName != null) return false; 221 222 return true; 223 } 224 225 @Override 226 public int hashCode() { 227 int result = packageName != null ? packageName.hashCode() : 0; 228 result = 31 * result + (className != null ? className.hashCode() : 0); 229 return result; 230 } 231}