blob: 8c8cb6114fd348d785987c062d9ac16f3438efe5 [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.internal.codegen;
import java.util.*;
/**
* INTERNAL:
* <p><b>Purpose</b>: Model a method for code generation purposes.
*
* @since TopLink 3.0
* @author James Sutherland
*/
public abstract class MethodDefinition extends CodeDefinition {
protected boolean isAbstract;
protected boolean isConstructor;
protected String returnType;
protected Vector<String> argumentNames;
protected Vector<String> lines;
protected Vector<String> exceptions;
protected StringBuffer storedBuffer;
protected MethodDefinition() {
this.isConstructor = false;
this.returnType = "void";
this.lines = new Vector<>();
this.exceptions = new Vector<>();
this.storedBuffer = new StringBuffer();
}
public void addException(String exceptionTypeName) {
this.exceptions.add(exceptionTypeName);
}
public void addLine(String line) {
this.storedBuffer.append(line);
getLines().addElement(this.storedBuffer.toString());
this.storedBuffer = new StringBuffer();
}
/**
* This method can be used to store a string that will be prepended to the very next line of code entered
*/
public void addToBuffer(String partOfLine) {
this.storedBuffer.append(partOfLine);
}
private void adjustExceptions(Map<String, Set<String>> typeNameMap) {
for (Iterator<String> i = new Vector<>(getExceptions()).iterator(); i.hasNext();) {
String exceptionName = i.next();
String adjustedExceptionName = adjustTypeName(exceptionName, typeNameMap);
if (!exceptionName.equals(adjustedExceptionName)) {
replaceException(exceptionName, adjustedExceptionName);
}
}
}
/**
* Parses the line, removing the package name for each type
* (and adding the appropriate import) if the type is
* unambiguous.
*/
private void adjustLine(String line, Map<String, Set<String>> typeNameMap) {
StringBuilder lineInProgress = new StringBuilder(line);
Set<String> typeNames = parseForTypeNames(lineInProgress.toString());
for (Iterator<String> i = typeNames.iterator(); i.hasNext();) {
String typeName = i.next();
String adjustedTypeName = adjustTypeName(typeName, typeNameMap);
if (!typeName.equals(adjustedTypeName)) {
int typeNameStartIndex = lineInProgress.toString().indexOf(typeName);
while (typeNameStartIndex != -1) {
lineInProgress.replace(typeNameStartIndex, typeNameStartIndex + typeName.length(), adjustedTypeName);
typeNameStartIndex = lineInProgress.toString().indexOf(typeName);
}
}
}
replaceLine(line, lineInProgress.toString());
}
private void adjustLines(Map<String, Set<String>> typeNameMap) {
for (Iterator<String> i = new Vector<>(getLines()).iterator(); i.hasNext();) {
adjustLine(i.next(), typeNameMap);
}
}
private void adjustReturnType(Map<String, Set<String>> typeNameMap) {
String adjustedReturnType = adjustTypeName(getReturnType(), typeNameMap);
if (!getReturnType().equals(adjustedReturnType)) {
setReturnType(adjustedReturnType);
}
}
protected void adjustTypeNames(Map<String, Set<String>> typeNameMap) {
adjustReturnType(typeNameMap);
adjustExceptions(typeNameMap);
adjustLines(typeNameMap);
}
protected abstract boolean argumentsEqual(MethodDefinition methodDefinition);
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!(object instanceof MethodDefinition)) {
return false;
}
MethodDefinition methodDefinition = (MethodDefinition)object;
if ((this.name == null) && (methodDefinition.getName() != null)) {
return false;
}
if ((this.name != null) && !this.name.equals(methodDefinition.getName())) {
return false;
}
if (!this.accessLevel.equals(methodDefinition.getAccessLevel())) {
return false;
}
if (!this.returnType.equals(methodDefinition.getReturnType())) {
return false;
}
if (!argumentsEqual(methodDefinition)) {
return false;
}
if (!exceptionsEqual(methodDefinition)) {
return false;
}
return true;
}
protected boolean exceptionsEqual(MethodDefinition methodDefinition) {
Object[] exceptions1 = this.getExceptions().toArray();
Object[] exceptions2 = methodDefinition.getExceptions().toArray();
if (exceptions1.length == exceptions2.length) {
for (int i = 0; i < exceptions1.length; i++) {
if (((exceptions1[i] == null) && (exceptions1[i] != exceptions2[i])) || (!exceptions1[i].equals(exceptions2[i]))) {
return false;
}
}
return true;
}
return false;
}
protected Vector<String> getArgumentNames() {
if (this.argumentNames == null) {
this.argumentNames = new Vector<>(5);
}
return argumentNames;
}
public String getArgumentName(int index) {
return getArgumentNames().get(index);
}
public Iterator<String> argumentNames() {
return getArgumentNames().iterator();
}
public int argumentNamesSize() {
return getArgumentNames().size();
}
protected abstract Vector<String> getArgumentTypeNames();
protected abstract Vector<String> getArgumentTypes();
public Vector<String> getLines() {
return lines;
}
protected Vector<String> getExceptions() {
return this.exceptions;
}
public String getReturnType() {
return returnType;
}
@Override
public int hashCode() {
int hash = this.accessLevel.hashCode();
hash ^= this.returnType.hashCode();
hash ^= this.getArgumentTypes().hashCode();
if (this.name != null) {
hash ^= this.name.hashCode();
}
if (this.name != null) {
hash ^= this.name.hashCode();
}
hash ^= this.getExceptions().hashCode();
return hash;
}
public boolean isAbstract() {
return this.isAbstract;
}
public boolean isConstructor() {
return isConstructor;
}
/**
* Used for calculating imports. @see org.eclipse.persistence.internal.codegen.ClassDefinition#calculateImports()
*/
protected void putTypeNamesInMap(Map<String, Set<String>> typeNameMap) {
putTypeNameInMap(getReturnType(), typeNameMap);
for (Iterator<String> i = getExceptions().iterator(); i.hasNext();) {
putTypeNameInMap(i.next(), typeNameMap);
}
for (Iterator<String> i = getArgumentTypeNames().iterator(); i.hasNext();) {
putTypeNameInMap(i.next(), typeNameMap);
}
}
protected void replaceException(String oldExceptionName, String newExceptionName) {
int index = getExceptions().indexOf(oldExceptionName);
getExceptions().remove(oldExceptionName);
getExceptions().insertElementAt(newExceptionName, index);
}
protected void replaceLine(String oldLine, String newLine) {
int index = getLines().indexOf(oldLine);
getLines().remove(oldLine);
getLines().insertElementAt(newLine, index);
}
public void setIsAbstract(boolean isAbstract) {
this.isAbstract = isAbstract;
}
public void setIsConstructor(boolean isConstructor) {
this.isConstructor = isConstructor;
}
public void setReturnType(String returnType) {
this.returnType = returnType;
}
/**
* Write the code out to the generator's stream.
*/
@Override
public void writeBody(CodeGenerator generator) {
if (!isConstructor()) {
generator.writeType(getReturnType());
generator.write(" ");
}
generator.write(getName());
generator.write("(");
writeArguments(generator);
generator.write(")");
if (!this.exceptions.isEmpty()) {
writeThrowsClause(generator);
}
if (isAbstract()) {
generator.write(";");
} else {
generator.write(" {");
generator.cr();
for (Enumeration<String> linesEnum = getLines().elements(); linesEnum.hasMoreElements();) {
generator.tab();
generator.writeln(linesEnum.nextElement());
}
generator.write("}");
}
}
protected abstract void writeArguments(CodeGenerator generator);
protected void writeThrowsClause(CodeGenerator generator) {
generator.write(" throws ");
for (Iterator<String> exceptionIterator = this.exceptions.iterator(); exceptionIterator.hasNext();) {
generator.write(exceptionIterator.next());
if (exceptionIterator.hasNext()) {
generator.write(", ");
}
}
}
}