RecordAnalyzer.java
package com.mackenziehigh.autumn.lang.compiler.utils;
import autumn.lang.Record;
import autumn.lang.internals.annotations.Getter;
import autumn.lang.internals.annotations.Setter;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.mackenziehigh.autumn.lang.compiler.typesystem.TypeFactory;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IDeclaredType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IMethod;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.ITypeFactory;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IVariableType;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* An instance of this class represents the basic bytecode representation of a record.
*
* @author Mackenzie High
*/
public final class RecordAnalyzer
{
/**
* This is the type of the record that is being compiled.
*/
public final IDeclaredType type;
/**
* These are the elements in the new record.
*/
public final Map<String, RecordElement> elements;
/**
* These are all the setter methods in the new record.
*/
public final Set<SetterMethod> setters = Sets.newHashSet();
/**
* These are the all getter methods in the record.
*/
public final Set<GetterMethod> getters = Sets.newHashSet();
/**
* These are the covariance violations detected in the record-type.
*/
public final Set<CovarianceViolation> violations;
/**
* Sole Constructor.
*
* @param type is the type of the record that is being compiled.
*/
public RecordAnalyzer(final IDeclaredType type)
{
Preconditions.checkNotNull(type);
this.type = type;
for (IMethod method : type.getAllVisibleMethods())
{
potentiallyAddSetter(method);
potentiallyAddGetter(method);
}
this.violations = CovarianceViolationDetector.detect(this);
addBaseSetters();
addBaseGetters();
this.elements = elements();
}
/**
* This method adds a setter method to the set of setters, if it is really a setter method.
*
* @param method is the method that may be a setter method.
*/
private void potentiallyAddSetter(final IMethod method)
{
/**
* A setter always takes exactly one argument.
*/
if (method.getParameters().size() != 1)
{
return;
}
/**
* A setter has a special annotation applied to it.
*/
if (TypeSystemUtils.isAnnotationPresent(method, autumn.lang.internals.annotations.Setter.class) == false)
{
return;
}
/**
* Create an object that represents the setter.
*/
final IDeclaredType returns = (IDeclaredType) method.getReturnType();
final String name = method.getName();
final IVariableType value = (IVariableType) method.getParameters().get(0).getType();
final SetterMethod setter = new SetterMethod(method.getOwner(), returns, name, value);
/**
* Remember the setter for later use.
*/
setters.add(setter);
}
/**
* This method adds a setter method to the set of getters, if it is really a getter method.
*
* @param method is the method that may be a getter method.
*/
private void potentiallyAddGetter(final IMethod method)
{
/**
* If the method takes parameters, then it is not a getter.
*/
if (method.getParameters().isEmpty() == false)
{
return;
}
/**
* A getter has a special annotation applied to it.
*/
if (TypeSystemUtils.isAnnotationPresent(method, autumn.lang.internals.annotations.Getter.class) == false)
{
return;
}
/**
* Create an object that represents the getter.
*/
final String name = method.getName();
final IVariableType value = (IVariableType) method.getReturnType();
final GetterMethod getter = new GetterMethod(method.getOwner(), value, name);
/**
* Remember the getter for later use.
*/
getters.add(getter);
}
private void addBaseSetters()
{
final List<SetterMethod> initial = Lists.newArrayList(setters);
for (SetterMethod setter : initial)
{
setters.add(new SetterMethod(type, type, setter.name, setter.parameter));
setters.add(new SetterMethod(type, setter.returns, setter.name, setter.parameter));
}
}
private void addBaseGetters()
{
final List<GetterMethod> initial = Lists.newArrayList(getters);
for (GetterMethod getter : initial)
{
final GetterMethod method = new GetterMethod(type, getter.returns, getter.name);
getters.add(method);
}
}
/**
* These are the setter methods that are needed in the record.
*
* @return an immutable set containing descriptions of the needed setters.
*/
public Set<SetterMethod> setters()
{
final Set<SetterMethod> result = Sets.newHashSet();
for (SetterMethod setter : setters)
{
if (setter.owner.equals(type))
{
result.add(setter);
}
}
return Collections.unmodifiableSet(result);
}
/**
* These are the getter methods that are needed in the record.
*
* @return an immutable set containing descriptions of the needed getters.
*/
public Set<GetterMethod> getters()
{
final Set<GetterMethod> result = Sets.newHashSet();
for (GetterMethod getter : getters)
{
if (getter.owner.equals(type))
{
result.add(getter);
}
}
return Collections.unmodifiableSet(result);
}
/**
* This method finds the names of the elements in the record-type.
*
* @return an immutable set containing the names of the elements in the record-type.
*/
private Set<String> names()
{
final Set<String> names = Sets.newTreeSet();
for (GetterMethod getter : getters)
{
names.add(getter.name);
}
return Collections.unmodifiableSet(names);
}
/**
* This method creates a map that maps the name of a element to a description of the element.
*
* @return the aforedescribed immutable map.
*/
public Map<String, RecordElement> elements()
{
final Map<String, RecordElement> map = Maps.newTreeMap();
for (String name : names())
{
map.put(name, new RecordElement(this, name));
}
return Collections.unmodifiableMap(map);
}
public static interface Person
extends Record
{
@Setter
public Person name(Object name);
@Getter
public Object name();
@Setter
public Person age(int age);
@Getter
public int age();
}
public static interface Taxable
{
@Setter
public Taxable name(String name);
@Getter
public String name();
}
public static interface Citizen
extends Person,
Taxable
{
@Setter
public Citizen name(String name);
@Getter
@Override
public String name();
@Setter
public Citizen age(int age);
@Getter
@Override
public int age();
}
public static interface M
{
@Setter
public M x(final Object value);
@Getter
public Object x();
}
public static class S
implements M
{
@Setter
public S x(final String value)
{
return null;
}
@Setter
@Override
public S x(Object value)
{
throw new UnsupportedOperationException("Not supported yet.");
}
@Getter
@Override
public String x()
{
return null;
}
}
public static void main(final String[] args)
{
final ITypeFactory factory = new TypeFactory();
final IDeclaredType type = (IDeclaredType) factory.fromClass(S.class);
final RecordAnalyzer record = new RecordAnalyzer(type);
final Set<String> set = Sets.newTreeSet();
for (SetterMethod setter : record.setters())
{
set.add(setter.toString());
}
for (GetterMethod getter : record.getters())
{
set.add(getter.toString());
}
for (String x : set)
{
System.out.println(x);
}
System.out.println();
for (CovarianceViolation violation : record.violations)
{
System.out.println(violation);
}
}
}