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