Skip to content

Commit

Permalink
todo handle "typing." in docs, ensure #items has dataTypeForDocs
Browse files Browse the repository at this point in the history
  • Loading branch information
dphuang2 committed Oct 12, 2023
1 parent 5b637db commit 7cf0da3
Show file tree
Hide file tree
Showing 6 changed files with 335 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
/**
* Copyright (c) 2012-2015 Edgar Espina
*
* This file is part of Handlebars.java.
*
* 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.
*/
package com.github.jknack.handlebars.context;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.util.LinkedHashSet;
import java.util.Set;

import com.github.jknack.handlebars.ValueResolver;
import com.github.jknack.handlebars.context.FieldValueResolver.FieldWrapper;

/**
* A specialization of {@link MemberValueResolver} with lookup and invocation
* support for {@link Field}.
* It matches private, protected, package, public and no-static field.
*
* @author edgar.espina
* @since 0.1.1
*/
public class FieldValueResolver extends MemberValueResolver<FieldWrapper> {

/**
* Workaround for accessing to the public attribute length of arrays.
*
* @author edgar.espina
* @since 0.12.0
*/
public interface FieldWrapper extends Member {
/**
* Returns the value of the field represented by this {@code Field}, on
* the specified object. The value is automatically wrapped in an
* object if it has a primitive type.
*
* <p>
* The underlying field's value is obtained as follows:
*
* <p>
* If the underlying field is a static field, the {@code obj} argument is ignored; it may be
* null.
*
* <p>
* Otherwise, the underlying field is an instance field. If the specified {@code obj} argument
* is null, the method throws a {@code NullPointerException}. If the specified object is not an
* instance of the class or interface declaring the underlying field, the method throws an
* {@code IllegalArgumentException}.
*
* <p>
* If this {@code Field} object enforces Java language access control, and the underlying field
* is inaccessible, the method throws an {@code IllegalAccessException}. If the underlying field
* is static, the class that declared the field is initialized if it has not already been
* initialized.
*
* <p>
* Otherwise, the value is retrieved from the underlying instance or static field. If the field
* has a primitive type, the value is wrapped in an object before being returned, otherwise it
* is returned as is.
*
* <p>
* If the field is hidden in the type of {@code obj}, the field's value is obtained according to
* the preceding rules.
*
* @param obj object from which the represented field's value is
* to be extracted
* @return the value of the represented field in object {@code obj}; primitive values are
* wrapped in an appropriate
* object before being returned
*
* @exception IllegalAccessException if the underlying field
* is inaccessible.
*/
Object get(Object obj) throws IllegalAccessException;
}

/**
* Use a {@link Field} as member.
*
* @author edgar.espina
* @since 0.12.0
*/
private static class FieldMember extends AccessibleObject implements FieldWrapper {

/**
* The field object.
*/
private Field field;

/**
* @param field The field object.
*/
FieldMember(final Field field) {
this.field = field;
}

@Override
public Class<?> getDeclaringClass() {
return field.getDeclaringClass();
}

@Override
public String getName() {
return field.getName();
}

@Override
public int getModifiers() {
return field.getModifiers();
}

@Override
public boolean isSynthetic() {
return field.isSynthetic();
}

@Override
public Object get(final Object obj) throws IllegalAccessException {
return field.get(obj);
}

@Override
public String toString() {
return field.toString();
}

@Override
public boolean isAccessible() {
return field.isAccessible();
}

@Override
public void setAccessible(final boolean flag) {
field.setAccessible(flag);
}
}

/**
* See http://stackoverflow.com/questions/11097658/getting-the-field-length-in-a-java-array-using
* -reflection.
*
* @author edgar.espina
* @since 0.12.0
*/
private static final class ArrayLengthMember implements FieldWrapper {

/**
* One instance is enough.
*/
public static final FieldWrapper LENGTH = new ArrayLengthMember();

/**
* Not allowed.
*/
private ArrayLengthMember() {
}

@Override
public Class<?> getDeclaringClass() {
return null;
}

@Override
public String getName() {
return "length";
}

@Override
public int getModifiers() {
return Modifier.PUBLIC;
}

@Override
public boolean isSynthetic() {
return false;
}

@Override
public Object get(final Object obj) throws IllegalAccessException {
return Array.getLength(obj);
}

}

/**
* The default value resolver.
*/
public static final ValueResolver INSTANCE = new FieldValueResolver();

@Override
public boolean matches(final FieldWrapper field, final String name) {
return !isStatic(field) && field.getName().equals(name);
}

@Override
protected Object invokeMember(final FieldWrapper field, final Object context) {
try {
return field.get(context);
} catch (Exception ex) {
throw new IllegalStateException(
"Shouldn't be illegal to access field '" + field.getName()
+ "'", ex);
}
}

@Override
protected Set<FieldWrapper> members(final Class<?> clazz) {
Set<FieldWrapper> members = new LinkedHashSet<>();
if (clazz.isArray()) {
members.add(ArrayLengthMember.LENGTH);
} else {
Class<?> targetClass = clazz;
do {
Field[] fields = targetClass.getDeclaredFields();
for (Field field : fields) {
FieldWrapper wrapper = new FieldMember(field);
if (matches(wrapper, memberName(wrapper))) {
members.add(wrapper);
}
}
targetClass = targetClass.getSuperclass();
} while (targetClass != null && targetClass != Object.class);
}
return members;
}

@Override
protected String memberName(final FieldWrapper member) {
return member.getName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ public String paramNameSnakeCase() {
// If this schema is an object type schema
public boolean isObject;

// If schema include anyOf/oneOf/allOf (e.g. is instanceof ComposedSchema)
public boolean isComposed;

// If this is an anyOf/oneOf schema that includes an object type schema
public boolean isComposedObject;

Expand All @@ -70,6 +73,9 @@ public String paramNameSnakeCase() {

// When you need "HashMap" instead of "Map" in Java SDK
public String dataTypeClass;

// When you need Dict instead of "typing.Dict" in Python SDK top-level README.md
public String dataTypeForDocs;
public boolean isList;
public boolean isHashMap;
public boolean isNotListOrHashMap;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4129,7 +4129,7 @@ public CodegenProperty fromProperty(String name, Schema p, boolean required,
LOGGER.debug("debugging from property return: {}", property);
schemaCodegenPropertyCache.put(ns, property);

if (property.dataType != null && property.isModel) {
if (property.dataType != null) {
property.modelFilename = toModelFilename((property.dataType));
}

Expand Down Expand Up @@ -7123,6 +7123,7 @@ public CodegenParameter fromFormProperty(String name, Schema propertySchema, Set

codegenParameter.baseType = codegenProperty.baseType;
codegenParameter.dataType = codegenProperty.dataType;
codegenParameter.dataTypeForDocs = codegenProperty.dataType.replace("typing.", "");
codegenParameter.dataTypeLowerCase = codegenProperty.dataType.toLowerCase();
codegenParameter.defaultValue = toDefaultParameterValue(propertySchema);
codegenParameter.baseName = codegenProperty.baseName;
Expand All @@ -7133,6 +7134,9 @@ public CodegenParameter fromFormProperty(String name, Schema propertySchema, Set
codegenParameter.isEnum = codegenProperty.isEnum;
codegenParameter._enum = codegenProperty._enum;
codegenParameter.allowableValues = codegenProperty.allowableValues;
codegenParameter.isComposed = ModelUtils.isComposedSchema(ps);
codegenParameter.isComposedObject = isComposedObject(ps);
codegenParameter.isStrictlyObject = isStrictlyObject(ps);

if (ModelUtils.isFileSchema(ps) && !ModelUtils.isStringSchema(ps)) {
// swagger v2 only, type file
Expand Down Expand Up @@ -7783,6 +7787,7 @@ public CodegenParameter fromRequestBody(RequestBody body, Set<String> imports, S
// For instantiation in Java SDK
codegenParameter.dataTypeClass = codegenParameter.dataType.replace("Map<", "HashMap<");
codegenParameter.dataTypeClass = codegenParameter.dataTypeClass.replace("List<", "ArrayList<");
codegenParameter.dataTypeForDocs = codegenParameter.dataType.replace("typing.", "");

codegenParameter.isHashMap = codegenParameter.dataTypeClass.startsWith("HashMap<");
codegenParameter.isList = codegenParameter.dataType.startsWith("List<");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,54 @@ asyncio.run(main())
{{> api_doc_example_execute_call}}
```

{{#if hasParams}}
{{#each allParamsWithRequestBodyProperties}}
{{#if @first}}
#### ⚙️ Parameters

{{/if}}
{{#if operationVendorExtensions.x-konfig-operation-can-have-single-parameter}}
##### {{paramName}}: {{> api_doc_param_data_type}}

{{> README_parameter_description}}
{{else}}
{{#unless isFromBodyParam}}
##### {{paramName}}: {{> api_doc_param_data_type}}

{{> README_parameter_description}}
{{/unless}}
{{/if}}
{{/each}}
{{#with bodyParam}}
{{#unless operationVendorExtensions.x-konfig-operation-can-have-single-parameter}}
##### requestBody: {{> api_doc_param_data_type}}

{{> README_parameter_description}}
{{else}}
{{#if isArray}}
#### ⚙️ Request Body

{{> api_doc_param_data_type}}

{{> README_parameter_description}}
{{/if}}
{{/unless}}
{{/with}}
{{/if}}
{{#with returnModel}}
#### 🔄 Return

[{{{name}}}](./models/{{{classFilename}}}.ts)

{{/with}}
#### 🌐 Endpoint

`{{path}}` `{{httpMethod}}`

[🔙 **Back to Table of Contents**](#table-of-contents)

---
{{/each}}
{{/with}}
{{/each}}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{{#if description}}
{{{description}}}

{{/if}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{{#if isPrimitiveType}}
`{{dataTypeForDocs}}`
{{else}}
{{#if isModel}}
{{#if hasVars}}
[`{{dataType}}`](./{{packageName}}/type/{{modelFilename}}.py)
[`{{dataTypeForDocs}}`](./{{packageName}}/type/{{modelFilename}}.py)
{{~else}}
{{#if isStrictlyObject}}
[`{{dataTypeForDocs}}`](./{{packageName}}/type/{{modelFilename}}.py)
{{else}}
`{{dataTypeForDocs}}`
{{~/if}}
{{/if}}
{{else}}
{{#if isArray}}
{{#with items}}
List[{{> api_doc_param_data_type}}]
{{~/with}}
{{else}}
[`{{dataTypeForDocs}}`](./{{packageName}}/type/{{modelFilename}}.py)
{{/if}}
{{/if}}
{{/if}}

0 comments on commit 7cf0da3

Please sign in to comment.