I am asking this question with my limited knowledge of java reflection and AOP.
Background:
I am using annotation based advice in my Java 7 application. Further to get the method parameter which I need to use in my advice I am using spring EL. See below examples:
In first example i want to use second parameter to do my work, whereas in second example I am using a POJO and want to use its "id" field.
#MyAnnotation(param = "args[1]")
public void someMethod(int param1, String param2) {
return null;
}
#MyAnnotation(param = "args[0].id")
public void someMethod(SomeObject someObject) {
return null;
}
But what I actually want is, to get my hands on the parameter names in my AOP. So that I can use #MyAnnotation(param = "param1") or #MyAnnotation(param = "someObject.id") instead.
From what I have known, you can not get parameter name using reflection. But recently I came across Spring cache abstraction(link), where I see:
#Cacheable(cacheNames="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
Can someone put some light here, how I can achieve similar behavior.
See MethodBasedEvaluationContext and ParameterNameDiscoverer.
CacheEvaluationContext is a subclass of MethodBasedEvaluationContext.
Code here...
// Expose indexed variables as well as parameter names (if discoverable)
String[] paramNames = this.parameterNameDiscoverer.getParameterNames(this.method);
int paramCount = (paramNames != null ? paramNames.length : this.method.getParameterCount());
int argsCount = this.arguments.length;
for (int i = 0; i < paramCount; i++) {
Object value = null;
if (argsCount > paramCount && i == paramCount - 1) {
// Expose remaining arguments as vararg array for last parameter
value = Arrays.copyOfRange(this.arguments, i, argsCount);
}
else if (argsCount > i) {
// Actual argument found - otherwise left as null
value = this.arguments[i];
}
setVariable("a" + i, value);
setVariable("p" + i, value);
if (paramNames != null) {
setVariable(paramNames[i], value);
}
}
Related
I'm playing around and developed a simple custom JsonConverter that takes a min and max temperature and have decorated my model class as follows and validates that the temperature falls in that range.
[JsonConverter(typeof(TemperatureConverter), 5, 10)]
public int Temperature { get; set; }
This is all good but I'm wondering what's the approach to best output the correct decoration in my swagger file generated by swashbuckle... like so:
name: Temperature
schema:
type: integer
minimum: 5
maximum: 10
I know this is a trivial example, but it's more the approach to tying JsonConverter to the generation of the swagger I'm interested in.
I'm currently looking at ISchemaFilter but can't see how I can get the type of converter that decorates the property.
Thanks
You have to be at the parent schema level, looking at it's properties. By the time it gets to the property itself, it is too late, as there is no link back to the parent class.
I was using a custom attribute, not JsonConverter, but something like this should work for detecting the attribute.
public class TemperatureSchemaFilter : ISchemaFilter
{
public void Apply(Schema schema, SchemaFilterContext context)
{
var converterProperties = context.SystemType.GetProperties().Where(
prop => prop.CustomAttributes.Select(
attr => attr.AttributeType == typeof(JsonConverterAttribute)).Any()
).ToList();
foreach (var converterProperty in converterProperties)
{
var converterAttribute = (JsonConverterAttribute)Attribute.GetCustomAttribute(converterProperty.PropertyType, typeof(JsonConverterAttribute));
if (converterAttribute.ConverterType != typeof(TemperatureConverter)) continue;
Schema propertySchema = null;
try
{
propertySchema = schema.Properties.First(x => x.Key.ToLower().Equals(converterProperty.Name.ToLower())).Value;
}
catch (Exception)
{
continue;
}
if (propertySchema == null) continue;
propertySchema.Minimum = (double) converterAttribute.ConverterParameters[0];
propertySchema.Maximum = (double) converterAttribute.ConverterParameters[1];
}
}
}
Unfortunately my environment is currently hosed, so I can't test it out, but I think this is the way to go.
I have an object as following :
public Class MyObjDTO {
private Long id;
private Boolean checked;
//getter and setters
#Override
public final int hashCode() {
Long id = getId();
return (id == null ? super.hashCode() : id.hashCode());
}
#Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (!(obj instanceof MyObjDTO))
return false;
Long id = getId();
Long objId = ((MyObjDTO) obj).getId();
if (id.equals(objId)) {
return true;
} else {
return false;
}
}
}
And I have two hash sets containing some instances from this object :
HashSet oldSet = new HashSet();
oldSet.add(new MyObjDTO(1,true));
oldSet.add(new MyObjDTO(2,true));
oldSet.add(new MyObjDTO(3,false));
HashSet newSet = new HashSet();
newSet.add(new MyObjDTO(1,false));
newSet.add(new MyObjDTO(2,true));
newSet.add(new MyObjDTO(4,true));
So what I want to do here is to select objects that are in the newSet and not in the oldSet, in this case its : new MyObjDTO(4,true) which I did using this :
Stream<MyObjDTO> toInsert = newSet.stream().filter(e -> !oldSet.contains(e));
Then I want to select objects that are in the oldSet and not in the newSet, in this case its :new MyObjDTO(3,false) which I did using this :
Stream<MyObjDTO> toRemove = oldSet.stream().filter(e -> !newSet.contains(e));
The last step is that I want to select the objects that are in both newSet and oldSet but they have a different value for the attribute checked , in this case it's : new MyObjDTO(1,false).
What I tried is this :
Stream<MyObjDTO> toUpdate = oldSet.stream().filter(newSet::contains);
But this one will return both new MyObjDTO(1,false) and new MyObjDTO(2,true).
How can I solve this ?
One way is to first use a map and then adjust your filter condition:
Map<MyObjDTO, Boolean> map = newSet.stream()
.collect(Collectors.toMap(Function.identity(), MyObjDTO::getChecked));
Stream<MyObjDTO> toUpdate = oldSet.stream()
.filter(old -> newSet.contains(old) && old.getChecked() != map.get(old));
Firstly, your equals() and hashCode() methods violate their basic contract. As per the javadoc of hashCode():
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
Your implementation of hashCode() does not follow this contract. Your first step should be to fix that.
Secondly, since Java 1.2 (nearly 20 years ago), java has provided the method removeAll() that does exactly what you want to do for the first part:
// Given these 2 sets:
HashSet<MyObjDTO> oldSet = new HashSet<>();
HashSet<MyObjDTO> newSet = new HashSet<>();
HashSet<MyObjDTO> onlyInNew = new HashSet<>(newSet);
onlyInNew.removeAll(oldSet);
// similar for onlyInOld
For the second part, you'll need to create a Map to find and get the object out:
Map<MyObjDTO, MyObjDTO> map = new HashMap<>O;
oldSet.forEach(o -> map.put(o, o);
HashSet<MyObjDTO> updated = new HashSet<>(newSet);
updated.removeIf(o -> oldSet.contains(o) && o.getChecked()() != map.get(o).getChecked());
In the last step, you rely on the equals() method of the DTO :
Stream<FonctionnaliteDTO> toUpdate = oldSet.stream().filter(newSet::contains);
The method uses only the id field to determinate object equality.
You don't want to do that.
You want to filter on a specific field : checked.
Besides, you should perform the operation on the result of the intersection of the two Sets.
Note that you should use simply Collection.retainAll() to compute the intersection between two collections:
Set<MyObjDTO> set = ...
Set<MyObjDTO> setTwo = ...
set.retainAll(setTwo);
Then you can filter objects that have both same id and checked value by using a double loop : for + iterator.
for (MyObjDTO dto : set){
for (Iterator<MyObjDTO> it = set.iterator(); it.hasNext();){
MyObjDTO otherDto = it.next();
if (otherDto.getId().equals(dto.getId()) &&
otherDto.getChecked() == dto.getChecked()){
it.remove();
}
}
}
You could do that with Stream but IHMO it could be less readable.
I want to create a custom attribute that will be applied to classDeclarations. I can enumerate attributes from other methods on the class, but not the classDeclaration itself because it is some sort of special method.
I know it is possible though because SysObsoleteAttribute (called from the kernel) is placed in classDeclarations all over.
In Classes\CustCustomerService\create I just copied the attributes to Classes\CustCustomerService\classDeclaration at the top for this test.
[AifDocumentCreateAttribute, SysEntryPointAttribute(true)]
class CustCustomerService extends AifDocumentService
{
}
I created a static method on a new class:
static public void AttribsOfSysEntryPointAttributeOnMethod
(
str _sNameOfClass,
str _sNameOfMethod,
str _nameOfAttribute
)
{
int nClassId;
SysDictMethod sdm;
Object attributeAsObject;
SysDictClass sysDictClass;
Array attribArray = new Array(Types::Class);
int i;
nClassId = Global::className2Id(_sNameOfClass);
sysDictClass = new SysDictClass(nClassId);
sdm = new SysDictMethod(UtilElementType::ClassInstanceMethod, nClassId, _sNameOfMethod);
attribArray = sdm.getAllAttributes();
if (attribArray)
{
for (i=1; i<=attribArray.lastIndex(); i++)
{
attributeAsObject = attribArray.value(i);
info(strFmt("[%3] Attrib Class Id: %1 [%2]", classIdGet(attributeAsObject), classId2Name(classIdGet(attributeAsObject)), _sNameOfMethod));
}
}
else
{
// Unable to get attributes, try another way
error(strFmt("Unable to retrieve attribute array for method %1", sdm.name()));
// It is, so let's try and enumerate ALL attributes and output them directly from class dec
sdm = sysDictClass.objectMethodObject(1);
if (attribArray)
{
for (i=1; i<=attribArray.lastIndex(); i++)
{
attributeAsObject = attribArray.value(i);
info(strFmt("[%3] Attrib Class Id: %1 [%2]", classIdGet(attributeAsObject), classId2Name(classIdGet(attributeAsObject)), _sNameOfMethod));
}
}
else
error(strFmt("Still unable to retrieve attribute array for method %1", sysDictClass.objectMethod(1)));
}
}
Then created a job to call it, and you can see how it works for one method, but not the other.
static void Job5(Args _args)
{
AttributeReflection::AttribsOfSysEntryPointAttributeOnMethod("CustCustomerService", "create", "SysEntryPointAttribute");
AttributeReflection::AttribsOfSysEntryPointAttributeOnMethod("CustCustomerService", "classDeclaration", "SysEntryPointAttribute");
}
Any ideas how to enumerate Attributes from the classDeclaration??
The classDeclaration is not a method and cannot be called. Hence your sysDictClass variable is null.
Googling reveals that the getAllAttributes method exits on DictClass:
attribArray = sdm ? sdm.getAllAttributes() : sysDictClass.getAllAttributes();
I am looking for a good way to implement paging in ormlite and I found another question, which has this snippet:
var data = db.Select<address>(predicate).Skip((int) pageNumber).Take((int) pageSize).ToList();
Problem with the above is that it gets back all the results and then does the skip and take on it which defeats the purpose of paging.
At another google groups post I have found the same problem and a sample in a github issue is mentioned as a solution but the URL no longer works. Does anyone know how to correctly page using servicestack?
Found the answer in ormlite's tests. Essentially we could use SqlExpressionVisitor's Limit() like this:
var result = db.Select<K>( q => q.Where(predicate).Limit(skip:5, rows:10 ) );
I built a higher-level wrapper if you prefer working with Page and PageSize:
public static class PagingExtensions
{
public static SqlExpression<T> Page<T>(this SqlExpression<T> exp, int? page, int? pageSize)
{
if (!page.HasValue || !pageSize.HasValue)
return exp;
if (page <= 0) throw new ArgumentOutOfRangeException("page", "Page must be a number greater than 0.");
if (pageSize <= 0) throw new ArgumentOutOfRangeException("pageSize", "PageSize must be a number greater than 0.");
int skip = (page.Value - 1) * pageSize.Value;
int take = pageSize.Value;
return exp.Limit(skip, take);
}
// http://stackoverflow.com/a/3176628/508681
public static int? LimitToRange(this int? value, int? inclusiveMinimum, int? inclusiveMaximum)
{
if (!value.HasValue) return null;
if (inclusiveMinimum.HasValue && value < inclusiveMinimum) { return inclusiveMinimum; }
if (inclusiveMaximum.HasValue && value > inclusiveMaximum) { return inclusiveMaximum; }
return value;
}
}
Then you can write your query as:
var results = Db.Select<K>(predicate.Page(request.Page, request.PageSize));
Or, using the additional helper method to keep Page and PageSize to sensical and (possibly) performant values:
var results = Db.Select<K>(predicate.Page(request.Page.LimitTo(1,null) ?? 1, request.PageSize.LimitTo(1,100) ?? 100);
Which will enforce reasonable limits on Page and PageSize
Im trying to extend the flex ArrayCollection to be able to search for an object containing specific data and give it back.
Here is my function:
public function getItemContaining(value: String): Object {
//Loop through the collection
for each(var i: Object in this) {
//Loop through fields
for(var j: String in i) {
//If field value is equal to input value
if(i[j] == value) {
return i;
}
}
}
//If not found
return null;
}
Problem is j is always null so the second loop never works. So I read flex loop descriptions and actually it should work just fine. What can possibly be the problem?
Try it like this:
for (var name:String in myObject){
trace(name + ":" + myObject[name];
}
Okay that was actually the same you were doing. The error must be in this line:
for each(var i: Object in this) {
Try using this:
for each(var i: Object in this.source) {
My first instinct would be to have a look at data type. You're setting up a loop declaring j:String and the symptom is that j is always null. This suggests to me that Flex is failing to interpret the elements of i as strings. If Flex only recognizes the elements of i as Objects (because all Strings are Objects, and Objects are the lowest common denominator), it would return null for j:String.
Try this for your inner loop:
for(var j: Object in i) {
//If field value is equal to input value
if(i[j] is String && (i[j] as String) == value) {
return i;
}
}
if you are using ArrayCollection as your datasource, you should look at using the IViewCursor interface. You can supply a custom compare function, or supply the fields top compare to. This interface is well documented with examples in adobe/livedocs
var _cursor:IViewCursor;
var _idSortField:SortField;
var _idSort:Sort = new Sort();
_idSortField = new SortField();
_idSortField.compareFunction = this.myCompareFunction;
_idSort.fields = [_idSortField];
myArrayCollection.sort = _idSort;
myArrayCollection.refresh();
_cursor = myArrayCollection.createCursor();
if (_cursor.findAny(search))
return _cursor;
if you are search for a value in a specific property, then its even easier. Here's the link to adobe livedocs on this topic