Skip to content

Commit

Permalink
CLI implementation passing first test
Browse files Browse the repository at this point in the history
  • Loading branch information
sigpwned committed Jan 7, 2025
1 parent 3f21e71 commit 77e6ff7
Show file tree
Hide file tree
Showing 26 changed files with 2,007 additions and 1,021 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,5 @@
import javax.lang.model.type.TypeMirror;

public interface ConversionExprFactory {
public Optional<String> generateConversionExpr(TypeMirror targetType, TypeMirror sourceType,
String sourceValue);
public Optional<String> generateConversionExpr(TypeMirror targetType, String sourceValue);
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,10 @@ public ConversionExprFactoryChain(List<ConversionExprFactory> links) {
}

@Override
public Optional<String> generateConversionExpr(TypeMirror targetType, TypeMirror sourceType,
String sourceValue) {
public Optional<String> generateConversionExpr(TypeMirror targetType, String sourceValue) {
for (ConversionExprFactory link : getLinks()) {
final Optional<String> maybeExpr =
link.generateConversionExpr(targetType, sourceType, sourceValue);
link.generateConversionExpr(targetType, sourceValue);
if (maybeExpr.isPresent())
return maybeExpr;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*-
* =================================LICENSE_START==================================
* rapier-core
* ====================================SECTION=====================================
* Copyright (C) 2024 - 2025 Andy Boothe
* ====================================SECTION=====================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ==================================LICENSE_END===================================
*/
package rapier.core.conversion.expr;

import static java.util.Objects.requireNonNull;
import java.util.List;
import java.util.Optional;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
import rapier.core.ConversionExprFactory;

public class ElementwiseListConversionExprFactory implements ConversionExprFactory {
private final Types types;
private final ConversionExprFactory elementConversionExprFactory;

public ElementwiseListConversionExprFactory(Types types,
ConversionExprFactory elementConversionExprFactory) {
this.types = requireNonNull(types);
this.elementConversionExprFactory = requireNonNull(elementConversionExprFactory);
}

@Override
public Optional<String> generateConversionExpr(TypeMirror targetType, String sourceValue) {
if (targetType.getKind() != TypeKind.DECLARED)
return Optional.empty();
final TypeElement targetElement = (TypeElement) getTypes().asElement(targetType);
final DeclaredType targetDeclaredType = (DeclaredType) targetType;

// Get type arguments
List<? extends TypeMirror> typeArguments = targetDeclaredType.getTypeArguments();

// Ensure there's exactly one type argument. There might be zero, but there really, really
// shouldn't be more than 1.
if (typeArguments.size() != 1)
return Optional.empty();

// Get the first type argument
final TypeMirror targetTypeArgument = typeArguments.get(0);
if (targetTypeArgument.getKind() != TypeKind.DECLARED)
return Optional.empty();

// Generate conversion expression for each element
final Optional<String> maybeElementConversionExpr =
getElementConversionExprFactory().generateConversionExpr(targetTypeArgument, "element");
if (maybeElementConversionExpr.isEmpty())
return Optional.empty();
final String elementConversionExpr = maybeElementConversionExpr.orElseThrow();

return Optional.of(sourceValue + ".stream().map(element -> " + elementConversionExpr
+ ").collect(java.util.stream.Collectors.toList())");
}

private Types getTypes() {
return types;
}

private ConversionExprFactory getElementConversionExprFactory() {
return elementConversionExprFactory;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,17 @@

import static java.util.Objects.requireNonNull;
import java.util.Optional;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
import rapier.core.ConversionExprFactory;
import rapier.core.util.AnnotationProcessing;

public class FromStringConversionExprFactory implements ConversionExprFactory {
private final Types types;
Expand All @@ -36,22 +41,36 @@ public FromStringConversionExprFactory(Types types) {
}

@Override
public Optional<String> generateConversionExpr(TypeMirror targetType, TypeMirror sourceType,
String sourceValue) {
// Only generate conversion expressions for String -> T conversions
if (!sourceType.toString().equals("java.lang.String")) {
public Optional<String> generateConversionExpr(TypeMirror targetType, String sourceValue) {
if (targetType.getKind() != TypeKind.DECLARED)
return Optional.empty();
}

// Get the TypeElement representing the declared type
final TypeElement targetElement = (TypeElement) getTypes().asElement(targetType);
final DeclaredType targetDeclaredType = (DeclaredType) targetType;

final Optional<ExecutableElement> maybeFromStringMethod =
AnnotationProcessing.findFromStringMethod(getTypes(), targetElement);
if (maybeFromStringMethod.isEmpty())
return Optional.empty();
// Look for the "fromString" method
for (Element enclosedElement : targetElement.getEnclosedElements()) {
if (enclosedElement.getKind() != ElementKind.METHOD)
continue;
final ExecutableElement methodElement = (ExecutableElement) enclosedElement;
if (methodElement.getSimpleName().contentEquals("fromString")
&& methodElement.getModifiers().contains(Modifier.PUBLIC)
&& methodElement.getModifiers().contains(Modifier.STATIC)
&& methodElement.getParameters().size() == 1
&& methodElement.getReturnType().getKind() != TypeKind.VOID) {

final ExecutableType methodType =
(ExecutableType) getTypes().asMemberOf(targetDeclaredType, methodElement);

if (!methodType.getParameterTypes().get(0).toString().equals("java.lang.String"))
continue;
if (!getTypes().isSameType(targetType, methodType.getReturnType()))
continue;

return Optional.of(targetType.toString() + ".fromString(" + sourceValue + ")");
}
}

return Optional.of(targetType.toString() + ".fromString(" + sourceValue + ")");
return Optional.empty();
}

private Types getTypes() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,36 +21,62 @@

import static java.util.Objects.requireNonNull;
import java.util.Optional;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
import rapier.core.ConversionExprFactory;
import rapier.core.util.AnnotationProcessing;

public class SingleArgumentConstructorConversionExprFactory implements ConversionExprFactory {
private final Types types;
private final TypeMirror sourceType;

public SingleArgumentConstructorConversionExprFactory(Types types) {
public SingleArgumentConstructorConversionExprFactory(Types types, TypeMirror sourceType) {
this.types = requireNonNull(types);
this.sourceType = requireNonNull(sourceType);
}

@Override
public Optional<String> generateConversionExpr(TypeMirror targetType, TypeMirror sourceType,
String sourceValue) {
// Get the TypeElement representing the declared type
public Optional<String> generateConversionExpr(TypeMirror targetType, String sourceValue) {
if (targetType.getKind() != TypeKind.DECLARED)
return Optional.empty();
final TypeElement targetElement = (TypeElement) getTypes().asElement(targetType);
final DeclaredType targetDeclaredType = (DeclaredType) targetType;

// Iterate through the enclosed elements to find the single-argument constructor
final Optional<ExecutableElement> maybeConstructor =
AnnotationProcessing.findSingleArgumentConstructor(getTypes(), targetElement, sourceType);
if (maybeConstructor.isEmpty())
return Optional.empty();
// Look for the single-argument constructor
for (Element enclosedElement : targetElement.getEnclosedElements()) {
if (enclosedElement.getKind() != ElementKind.CONSTRUCTOR)
continue;
final ExecutableElement constructorElement = (ExecutableElement) enclosedElement;
if (constructorElement.getModifiers().contains(Modifier.PUBLIC)
&& constructorElement.getParameters().size() == 1) {

final ExecutableType constructorType =
(ExecutableType) getTypes().asMemberOf(targetDeclaredType, constructorElement);

if (!getTypes().isSameType(getSourceType(), constructorType.getParameterTypes().get(0)))
continue;
if (!getTypes().isSameType(targetType, constructorType.getReturnType()))
continue;

return Optional.of("new " + targetType.toString() + "(" + sourceValue + ")");
return Optional.of("new " + targetType.toString() + "(" + sourceValue + ")");
}
}

return Optional.empty();
}

private Types getTypes() {
return types;
}

private TypeMirror getSourceType() {
return sourceType;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,36 +21,65 @@

import static java.util.Objects.requireNonNull;
import java.util.Optional;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
import rapier.core.ConversionExprFactory;
import rapier.core.util.AnnotationProcessing;

public class ValueOfConversionExprFactory implements ConversionExprFactory {
private final Types types;
private final TypeMirror sourceType;

public ValueOfConversionExprFactory(Types types) {
public ValueOfConversionExprFactory(Types types, TypeMirror sourceType) {
this.types = requireNonNull(types);
this.sourceType = requireNonNull(sourceType);
}

@Override
public Optional<String> generateConversionExpr(TypeMirror targetType, TypeMirror sourceType,
String sourceValue) {
// Get the TypeElement representing the declared type
public Optional<String> generateConversionExpr(TypeMirror targetType, String sourceValue) {
if (targetType.getKind() != TypeKind.DECLARED)
return Optional.empty();
final TypeElement targetElement = (TypeElement) getTypes().asElement(targetType);
final DeclaredType targetDeclaredType = (DeclaredType) targetType;

// Iterate through the enclosed elements to find the "valueOf" method
final Optional<ExecutableElement> maybeValueOfMethod =
AnnotationProcessing.findValueOfMethod(getTypes(), targetElement, sourceType);
if (maybeValueOfMethod.isEmpty())
return Optional.empty();
// Look for the "fromString" method
for (Element enclosedElement : targetElement.getEnclosedElements()) {
if (enclosedElement.getKind() != ElementKind.METHOD)
continue;
final ExecutableElement methodElement = (ExecutableElement) enclosedElement;
if (methodElement.getSimpleName().contentEquals("valueOf")
&& methodElement.getModifiers().contains(Modifier.PUBLIC)
&& methodElement.getModifiers().contains(Modifier.STATIC)
&& methodElement.getParameters().size() == 1
&& methodElement.getReturnType().getKind() != TypeKind.VOID) {

final ExecutableType methodType =
(ExecutableType) getTypes().asMemberOf(targetDeclaredType, methodElement);

if (!getTypes().isSameType(getSourceType(), methodType.getParameterTypes().get(0)))
continue;
if (!getTypes().isSameType(targetType, methodType.getReturnType()))
continue;

return Optional.of(targetType.toString() + ".valueOf(" + sourceValue + ")");
return Optional.of(targetType.toString() + ".valueOf(" + sourceValue + ")");
}
}

return Optional.empty();
}

private Types getTypes() {
return types;
}

private TypeMirror getSourceType() {
return sourceType;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ public static Optional<ExecutableElement> findFromStringMethod(Types types,
&& method.getModifiers().contains(Modifier.PUBLIC)
&& method.getModifiers().contains(Modifier.STATIC) && method.getParameters().size() == 1
&& method.getParameters().get(0).asType().toString().equals("java.lang.String")
&& types.isSameType(method.getReturnType(), typeElement.asType())) {
&& method.getReturnType().getKind() != TypeKind.VOID) {
return Optional.of(method);
}
}
Expand Down
Loading

0 comments on commit 77e6ff7

Please sign in to comment.