I'm using PMD to generate some code quality report on a project.
I don't understand a result for the NPath complexity inspection.
I have created a dull class that is show-casing the result (this is not the real class, but it is using the same pattern):
import java.util.*;
public class SOFExample {
private final Map<String, Date> magicMap = new HashMap<String, Date>();
protected static final long UNKNWOWN = 0L;
private static final class MyCal { long aTime; long bTime; long cTime; long dTime;}
public void usefullMethod(final List<MyCal> myCals) {
final Date a = magicMap.get("a");
final Date b = magicMap.get("b");
final Date c = magicMap.get("c");
final Date d = magicMap.get("d");
final long aTime = a == null ? UNKNWOWN : a.getTime();
final long bTime = b == null ? UNKNWOWN : b.getTime();
final long cTime = c == null ? UNKNWOWN : c.getTime();
final long dTime = d == null ? UNKNWOWN : d.getTime();
for (MyCal myCal : myCals) {
if(myCal.aTime == UNKNWOWN) myCal.aTime = aTime;
if(myCal.bTime == UNKNWOWN) myCal.bTime = bTime;
if(myCal.cTime == UNKNWOWN) myCal.cTime = cTime;
if(myCal.dTime == UNKNWOWN) myCal.dTime = dTime;
}
}
}
PMD result:
The method usefullMethod() has an NPath complexity of 10625
If I add a new variable initialized the same way, I got this:
The method usefullMethod() has an NPath complexity of 103125
And if I replace all? With if-else structure, then I got this:
The method usefullMethod() has an NPath complexity of 1056
Why do I got this very high result with the ternary '?' Operator?
What's wrong with this code? (In this demo code it is easy to extract a method for getting the default values, but in real code it might not be possible)
Making the example even simpler, this class has a nPath value of 2. It should be pretty apparent why it is two - there are clearly two execution paths through the code.
package test;
import java.util.*;
public class Test {
private static final long UNKNWOWN = -1;
public void method(Date a) {
long aTime;
if (a == null) {
aTime = UNKNWOWN;
} else {
aTime = a.getTime();
}
}
}
And this class has a nPath value of 5. The question is why - there are still two logical paths through the code.
package test;
import java.util.*;
public class Test {
private static final long UNKNWOWN = -1;
public void method(Date a) {
final long aTime = a == null ? UNKNWOWN : a.getTime();
}
}
However, the algorithm used is as follows:
int npath = complexitySumOf(node, 0, data);
npath += 2;
It adds the complexity of all children then adds two for ternary. The minimum complexity returned for simple java nodes is 1. The AbstractSyntaxTree shows there are three children. Hence 3 + 2 is 5.
<ConditionalExpression beginColumn="36" beginLine="11" endColumn="69" endLine="11" ternary="true">
<EqualityExpression beginColumn="36" beginLine="11" endColumn="44" endLine="11" image="==">
<PrimaryExpression beginColumn="36" beginLine="11" endColumn="36" endLine="11">
<PrimaryPrefix beginColumn="36" beginLine="11" endColumn="36" endLine="11">
<Name beginColumn="36" beginLine="11" endColumn="36" endLine="11" image="a"/>
</PrimaryPrefix>
</PrimaryExpression>
<PrimaryExpression beginColumn="41" beginLine="11" endColumn="44" endLine="11">
<PrimaryPrefix beginColumn="41" beginLine="11" endColumn="44" endLine="11">
<Literal beginColumn="41" beginLine="11" charLiteral="false" endColumn="44" endLine="11" floatLiteral="false" intLiteral="false" singleCharacterStringLiteral="false" stringLiteral="false">
<NullLiteral beginColumn="41" beginLine="11" endColumn="44" endLine="11"/>
</Literal>
</PrimaryPrefix>
</PrimaryExpression>
</EqualityExpression>
<Expression beginColumn="48" beginLine="11" endColumn="55" endLine="11">
<PrimaryExpression beginColumn="48" beginLine="11" endColumn="55" endLine="11">
<PrimaryPrefix beginColumn="48" beginLine="11" endColumn="55" endLine="11">
<Name beginColumn="48" beginLine="11" endColumn="55" endLine="11" image="UNKNWOWN"/>
</PrimaryPrefix>
</PrimaryExpression>
</Expression>
<PrimaryExpression beginColumn="59" beginLine="11" endColumn="69" endLine="11">
<PrimaryPrefix beginColumn="59" beginLine="11" endColumn="67" endLine="11">
<Name beginColumn="59" beginLine="11" endColumn="67" endLine="11" image="a.getTime"/>
</PrimaryPrefix>
<PrimarySuffix argumentCount="0" arguments="true" arrayDereference="false" beginColumn="68" beginLine="11" endColumn="69" endLine="11">
<Arguments argumentCount="0" beginColumn="68" beginLine="11" endColumn="69" endLine="11"/>
</PrimarySuffix>
</PrimaryExpression>
</ConditionalExpression>
If you have a complex expression in the the ternary operator, the difference it count would be even more prevalent. As far as what's wrong with the code, it already has 9 branches (8 ternary operators and a loop) which is high even without the whole nPath calculation. I would refactor it regardless.
Related
How to create hive object that includes an id field with auto increment integer value?
This is my hive object class and id always is -1. How should I fix it?
import 'package:hive_flutter/adapters.dart';
part 'reminder.g.dart';
#HiveType(typeId : 2 )
class Reminder extends HiveObject {
int id = -1;
#HiveField(0)
String title = '';
#HiveField(1)
String body = '';
#HiveField(2)
String dateFancyString = '' ;
#HiveField(3)
String timeFancyString = '' ;
#HiveField(4)
DateTime dateTime = DateTime.now();
}
I'm checking the implementation of Map. (map.dart in dart:collection)
I find void clear(); in abstract class Map<K, V>. clear() doesn't have any implementation and class Map doesn't extend/implement any other classes. But I can still call clear().
example
Map<int, int> m = <int, int>{1: 1, 2: 2};
m.clear();
Where can I find the implementation of clear()?
My answer is based on the assumption that you want the implementation used when Dart is running natively and not on the web.
The default Map in Dart is a LinkedHashMap. There are several layers before getting the implementation of clear() but I expect this is the one you are looking for:
void clear() {
if (!isEmpty) {
_index = _uninitializedIndex;
_hashMask = _HashBase._UNINITIALIZED_HASH_MASK;
_data = _uninitializedData;
_usedData = 0;
_deletedKeys = 0;
}
}
https://github.com/dart-lang/sdk/blob/2.15.1/sdk/lib/_internal/vm/lib/compact_hash.dart#L333-L341
The {} notation creates a LinkedHashMap and that's where you'll find the implementation. See:
import 'dart:collection';
void main () {
Map<int, int> m = <int, int>{1: 1, 2: 2};
if(m is LinkedHashMap) {
print("It's a LinkedHashMap!");
}
}
Output:
It's a LinkedHashMap!
I've built an extension for Microsoft.EntityFrameworkCore that implements the AddOrUpdateMethod. It's working fine, but with entities with a composite primary key the AnyAsync method return always false, even if there are objects with the same key.
This is the method:
public static async Task AddOrUpdateAsync<TEntity>(this DbSet<TEntity> table, Expression<Func<TEntity, object>> key, Expression<Func<TEntity, bool>> deleteExpression, params TEntity[] entities) where TEntity : class
{
var getKeyFunction = key.Compile();
var getShouldDeleteFunction = deleteExpression.Compile();
var context = GetDbContext(table);
foreach (var entity in entities)
{
var primaryKey = getKeyFunction(entity);
var body = Expression.Equal(Expression.Convert(key.Body, primaryKey.GetType()), Expression.Constant(primaryKey));
Expression<Func<TEntity, bool>> query = Expression.Lambda<Func<TEntity, bool>>(body, key.Parameters);
var exist = await table.AnyAsync(query);
context.Entry(entity).State = exist
? getShouldDeleteFunction(entity) ? EntityState.Deleted : EntityState.Modified
: getShouldDeleteFunction(entity) ? EntityState.Detached : EntityState.Added;
}
}
private static DbContext GetDbContext<T>(this DbSet<T> table) where T : class
{
var infrastructure = table as IInfrastructure<IServiceProvider>;
var serviceProvider = infrastructure.Instance;
var currentDbContext = serviceProvider.GetService(typeof(ICurrentDbContext)) as ICurrentDbContext;
return currentDbContext.Context;
}
and I'm using it like this:
await db.Reports.AddOrUpdateAsync(r => new { r.Number, r.Year }, r => r.Active == false, response.Reports.ToArray());
I think that's happening because I'm using an anonymous type as the key, but I've no idea how to fix this.
The problem seems to be the usage of the anonymous type constant expression, which currently is causing client evaluation, and C# operator == compares anonymous types by reference, hence always returns false.
The trick to get the desired server translation is to "invoke" the key expression with entity by replacing the parameter with Expression.Constant(entity) (Expression.Invoke doesn't work in this case)
So remove the line var getKeyFunction = key.Compile(); as no longer needed, and use the following:
foreach (var entity in entities)
{
var parameter = key.Parameters[0];
var body = Expression.Equal(
key.Body,
key.Body.ReplaceParameter(parameter, Expression.Constant(entity))
);
var query = Expression.Lambda<Func<TEntity, bool>>(body, parameter);
var exist = await table.AnyAsync(query);
// ...
}
where ReplaceParameter is the usual expression helper method:
public static partial class ExpressionUtils
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
=> new ParameterReplacer { Source = source, Target = target }.Visit(expression);
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
=> node == Source ? Target : node;
}
}
I have a loop traversing a graph using a 'const' reference but when I assign my iteration variable I realize that it is non-const then I get nice compiler's complains.
class ClassDescriptor
{
const string name;
const TypeInfo type;
const ClassDescriptor base;
const IPropertyDescriptor[string] propertiesByName;
IPropertyDescriptor getFlattenProperty(string name)
{
// This declaration makes 'const(ClassDescriptor) bag'
// Note that in this point I can't add ref keyword.
auto bag = this;
while(!(bag is null))
{
if(name in bag.propertiesByName)
{
return bag.propertiesByName[name];
}
// This assigment breaks the constness
bag = bag.base;
}
return null;
}
public this(string name, TypeInfo type, ClassDescriptor base, const IPropertyDescriptor[string] propertiesByName)
{
this.name = name;
this.type = type;
this.base = base;
this.propertiesByName = propertiesByName;
}
}
Sounds like a job for std.typecons.Rebindable:
http://dpldocs.info/experimental-docs/std.typecons.Rebindable.html
import std.typecons;
Rebindable!(const typeof(this)) bag = this;
then the rest should work the same.
I have the following extension method
public static class ListExtensions
{
public static IEnumerable<T> Search<T>(this ICollection<T> collection, string stringToSearch)
{
foreach (T t in collection)
{
Type k = t.GetType();
PropertyInfo pi = k.GetProperty("Name");
if (pi.GetValue(t, null).Equals(stringToSearch))
{
yield return t;
}
}
}
}
What it does is by using reflection, it finds the name property and then filteres the record from the collection based on the matching string.
This method is being called as
List<FactorClass> listFC = new List<FactorClass>();
listFC.Add(new FactorClass { Name = "BKP", FactorValue="Book to price",IsGlobal =false });
listFC.Add(new FactorClass { Name = "YLD", FactorValue = "Dividend yield", IsGlobal = false });
listFC.Add(new FactorClass { Name = "EPM", FactorValue = "emp", IsGlobal = false });
listFC.Add(new FactorClass { Name = "SE", FactorValue = "something else", IsGlobal = false });
List<FactorClass> listFC1 = listFC.Search("BKP").ToList();
It is working fine.
But a closer look into the extension method will reveal that
Type k = t.GetType();
PropertyInfo pi = k.GetProperty("Name");
is actually inside a foreach loop which is actually not needed. I think we can take it outside the loop.
But how?
PLease help. (C#3.0)
Using reflection in this way is ugly to me.
Are you sure you need a 100% generic "T" and can't use a base class or interface?
If I were you, I would consider using the .Where<T>(Func<T, Boolean>) LINQ method instead of writing your own Search function.
An example use is:
List<FactorClass> listFC1 = listFC.Where(fc => fc.Name == "BKP").ToList();
public static IEnumerable<T> Search<T>(this ICollection<T> collection, string stringToSearch)
{
Type k = typeof(T);
PropertyInfo pi = k.GetProperty("Name");
foreach (T t in collection)
{
if (pi.GetValue(t, null).Equals(stringToSearch))
{
yield return t;
}
}
}
There's a couple of things you could do -- first you could institute a constraint on the generic type to an interface that has a name property. If it can only take a FactorClass, then you don't really need a generic type -- you could make it an extension to an ICollection<FactorClass>. If you go the interface route (or with the non-generic version), you can simply reference the property and won't have a need for reflection. If, for some reason, this doesn't work you can do:
var k = typeof(T);
var pi = k.GetProperty("Name");
foreach (T t in collection)
{
if (pi.GetValue(t, null).Equals(stringToSearch))
{
yield return t;
}
}
using an interface it might look like
public static IEnumerable<T> Search<T>(this ICollection<T> collection, string stringToSearch) where T : INameable
{
foreach (T t in collection)
{
if (string.Equals( t.Name, stringToSearch))
{
yield return t;
}
}
}
EDIT: After seeing #Jeff's comment, this is really only useful if you're doing something more complex than simply checking the value against one of the properties. He's absolutely correct in that using Where is a better solution for that problem.
Just get the type of T
Type k = typeof(T);
PropertyInfo pi = k.GetProperty("Name");
foreach (T t in collection)
{
if (pi.GetValue(t, null).Equals(stringToSearch))
{
yield return t;
}
}