Related
I had implement a interceptor of myabtis. but we found a problem, execute interceptor lead to throw so many IllegalAccessException, it affects cpu performence
Shown below is where the problem is, why did not check access permision of feild befor executed code "field.get(target)".
public class GetFieldInvoker implements Invoker {
private final Field field;
public GetFieldInvoker(Field field) {
this.field = field;
}
#Override
public Object invoke(Object target, Object[] args) throws IllegalAccessException {
try {
return field.get(target);
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
field.setAccessible(true);
return field.get(target);
} else {
throw e;
}
}
}
#Override
public Class<?> getType() {
return field.getType();
}
}
the intercepor of mine:
#Intercepts({
#Signature(
type = StatementHandler.class,
method = "prepare",
args = {Connection.class, Integer.class})
})
public class SqlIdInterceptor implements Interceptor {
private static final int MAX_LEN = 256;
private final RoomboxLogger logger = RoomboxLogManager.getLogger();
#Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = realTarget(invocation.getTarget());
MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
String originalSql = boundSql.getSql();
MappedStatement mappedStatement =
(MappedStatement) metaObject.getValue("delegate.mappedStatement");
String id = mappedStatement.getId();
if (id != null) {
int len = id.length();
if (len > MAX_LEN) {
logger.warn("too long id", "id", id, "len", len);
}
}
String newSQL = "# " + id + "\n" + originalSql;
metaObject.setValue("delegate.boundSql.sql", newSQL);
return invocation.proceed();
}
#SuppressWarnings("unchecked")
public static <T> T realTarget(Object target) {
if (Proxy.isProxyClass(target.getClass())) {
MetaObject metaObject = SystemMetaObject.forObject(target);
return realTarget(metaObject.getValue("h.target"));
}
return (T) target;
}
}
Flame Graph
enter image description here
enter image description here
I need help, how to avoid throw exceptions, is any other way to reslove this problem?
thanks.
I have the below code snippet for dynamic sorting using JPA Criteria API
Root<Employee> root = criteriaQuery.from(Employee);
Join<Employee, Project> joinProject =
root.join(Employee_.projectList, JoinType.LEFT);
if (sortDirection.equals("asc")) {
criteriaQuery.orderBy(cb.asc(root.get(sortField)));
If I am passing an attribute of Employee entity to order by statement, it works without any hitch, however if an attribute of Project entity is passed to order by statement, exception is thrown stating that
The attribute [projectName] is not present in the managed type
because projectName is an attribute of Projectentity which is joined with Employee using joinProject. In order by statement I am using root.get(sortField). if it is joinProject.get(sortField), it would work fine when attributes of Project are being passed to order by statement.
My questions are
How could I modify my Order By statement in order to cater all the attributes which being passed?
Do I need to conditionally check which attribute and accordingly use if conditions or are there better ways of doing this?
Appreciate insight into this.
A specific approach for a simple scenario (predetermined one-level only joins) may be something like this:
Root<Employee> root = criteriaQuery.from(Employee.class);
Join<Employee, Project> joinProject = root.join(Employee_.projectList, JoinType.LEFT);
Class<?> baseClass = fieldTypeMap.get(sortField);
From<?, ?> from;
if(baseClass == Employee.class)
{
from = root;
}
else if(baseClass == Project.class)
{
from = joinTeam;
}
else ...
Expression<?> expr = from.get(sortField);
if(sortDirection.equals("asc"))
{
criteriaQuery.orderBy(cb.asc(expr));
}
...
where fieldTypeMap is something like:
private final static Map<String, Class<?>> fieldTypeMap = new HashMap<>();
static {
fieldTypeMap.put("employeeName", Employee.class);
fieldTypeMap.put("projectName", Project.class);
...
}
However, this is quick and dirty, ugly and unmaintainable.
If you want a generic approach, things may get a bit complex.
Personally, I'm using my own classes built on top of EntityManager, CriteriaBuilder and Metamodel, which provides dynamic filtering and multi-sorting features.
But something like this should be meaningful enough:
protected static List<Order> buildOrderBy(CriteriaBuilder builder, Root<?> root, List<SortMeta> sortList)
{
List<Order> orderList = new LinkedList<>();
for(SortMeta sortMeta : sortList)
{
String sortField = sortMeta.getSortField();
SortOrder sortOrder = sortMeta.getSortOrder();
if(sortField == null || sortField.isEmpty() || sortOrder == null)
{
continue;
}
Expression<?> expr = getExpression(root, sortField);
if(sortOrder == SortOrder.ASCENDING)
{
orderList.add(builder.asc(expr));
}
else if(sortOrder == SortOrder.DESCENDING)
{
orderList.add(builder.desc(expr));
}
}
return orderList;
}
protected static Expression<?> getExpression(Root<?> root, String sortField)
{
ManagedType<?> managedType = root.getModel();
From<?, Object> from = (From<?, Object>) root;
String[] elements = sortField.split("\\.");
for(String element : elements)
{
Attribute<?, ?> attribute = managedType.getAttribute(element);
if(attribute.getPersistentAttributeType() == PersistentAttributeType.BASIC)
{
return from.get(element);
}
from = from.join(element, JoinType.LEFT);
managedType = EntityUtils.getManagedType(from.getJavaType());
}
return from;
}
This way the join is based on sort field, which is now a dotted-path-expr like "projectList.name" or "office.responsible.age"
public static <X> ManagedType<X> getManagedType(Class<X> clazz)
{
try
{
return getMetamodel().managedType(clazz);
}
catch(IllegalArgumentException e)
{
return null;
}
}
public static Metamodel getMetamodel()
{
return getEntityManagerFactory().getMetamodel();
}
public static EntityManagerFactory getEntityManagerFactory()
{
try
{
return InitialContext.doLookup("java:module/persistence/EntityManagerFactory");
}
catch(NamingException e)
{
throw new RuntimeException(e.getMessage(), e);
}
}
to make it work on a webapp, you have to declare contextual references on web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>my_app_name</display-name>
...
<persistence-context-ref>
<persistence-context-ref-name>java:module/persistence/EntityManager</persistence-context-ref-name>
<persistence-unit-name>my_pu_name</persistence-unit-name>
</persistence-context-ref>
<persistence-unit-ref>
<persistence-unit-ref-name>java:module/persistence/EntityManagerFactory</persistence-unit-ref-name>
<persistence-unit-name>my_pu_name</persistence-unit-name>
</persistence-unit-ref>
</web-app>
update
I don't know about how EclipseLink handles grouping, but also Hibernate does not perform joins (actually, conditions on joins) correctly in some case.
In example, when all these conditions are met:
querying an entity (as Root/From) that is part of a class hierarchy (and not a leaf) based on SINGLE_TABLE
joining/getting a property of a subclass
joining/getting by property name (String) instead of Attribute
To workaround the issue I always resolve property name to an Attribute and reuse joins already "walked" (did I say things may get complicated?):
public class MetaDescriptor extends BusinessObject implements Serializable, MemberEx, ColumnDescriptor
{
private static final long serialVersionUID = 1L;
#BusinessKey
protected final Attribute<?, ?> attribute;
#BusinessKey
protected final MetaDescriptor parent;
protected List<MetaDescriptor> childList;
protected final Type<?> elementType;
...
protected MetaDescriptor(Attribute<?, ?> attribute, MetaDescriptor parent)
{
this.attribute = attribute;
this.parent = parent;
if(attribute instanceof SingularAttribute)
{
SingularAttribute<?, ?> singularAttribute = (SingularAttribute<?, ?>) attribute;
elementType = singularAttribute.getType();
}
else if(attribute instanceof PluralAttribute)
{
PluralAttribute<?, ?, ?> pluralAttribute = (PluralAttribute<?, ?, ?>) attribute;
elementType = pluralAttribute.getElementType();
}
else
{
elementType = null;
}
}
public static MetaDescriptor getDescriptor(ManagedType<?> managedType, String path)
{
return getDescriptor(managedType, path, null);
}
public static MetaDescriptor getDescriptor(From<?, ?> from, String path)
{
if(from instanceof Root)
{
return getDescriptor(((Root<?>) from).getModel(), path);
}
return getDescriptor(from.getJavaType(), path);
}
public static MetaDescriptor getDescriptor(Class<?> clazz, String path)
{
ManagedType<?> managedType = EntityUtils.getManagedType(clazz);
if(managedType == null)
{
return null;
}
return getDescriptor(managedType, path);
}
private static MetaDescriptor getDescriptor(ManagedType<?> managedType, String path, MetaDescriptor parent)
{
if(path == null)
{
return null;
}
Entry<String, String> slice = StringUtilsEx.sliceBefore(path, '.');
String attributeName = slice.getKey();
Attribute<?, ?> attribute;
if("class".equals(attributeName))
{
attribute = new ClassAttribute<>(managedType);
}
else
{
try
{
attribute = managedType.getAttribute(attributeName);
}
catch(IllegalArgumentException e)
{
Class<?> managedClass = managedType.getJavaType();
// take only if it is unique
attribute = StreamEx.of(EntityUtils.getMetamodel().getManagedTypes())
.filter(x -> managedClass.isAssignableFrom(x.getJavaType()))
.flatCollection(ManagedType::getDeclaredAttributes)
.filterBy(Attribute::getName, attributeName)
.limit(2)
.collect(Collectors.reducing((a, b) -> null))
.orElse(null);
if(attribute == null)
{
return null;
}
}
}
MetaDescriptor descriptor = new MetaDescriptor(attribute, parent);
String remainingPath = slice.getValue();
if(remainingPath.isEmpty())
{
return descriptor;
}
Type<?> elementType = descriptor.getElementType();
if(elementType instanceof ManagedType)
{
return getDescriptor((ManagedType<?>) elementType, remainingPath, descriptor);
}
throw new IllegalArgumentException();
}
#Override
public <T> Expression<T> getExpression(CriteriaBuilder builder, From<?, ?> from)
{
From<?, Object> parentFrom = getParentFrom(from);
if(attribute instanceof ClassAttribute)
{
return (Expression<T>) parentFrom.type();
}
if(isSingular())
{
return parentFrom.get((SingularAttribute<Object, T>) attribute);
}
return getJoin(parentFrom, JoinType.LEFT);
}
private <X, T> From<X, T> getParentFrom(From<?, ?> from)
{
return OptionalEx.of(parent)
.map(x -> x.getJoin(from, JoinType.LEFT))
.select(From.class)
.orElse(from);
}
public <X, T> Join<X, T> getJoin(From<?, ?> from, JoinType joinType)
{
From<?, X> parentFrom = getParentFrom(from);
Join<X, T> join = (Join<X, T>) StreamEx.of(parentFrom.getJoins())
.findAny(x -> Objects.equals(x.getAttribute(), attribute))
.orElseGet(() -> buildJoin(parentFrom, joinType));
return join;
}
private <X, T> Join<X, T> buildJoin(From<?, X> from, JoinType joinType)
{
if(isSingular())
{
return from.join((SingularAttribute<X, T>) attribute, joinType);
}
if(isMap())
{
return from.join((MapAttribute<X, ?, T>) attribute, joinType);
}
if(isSet())
{
return from.join((SetAttribute<X, T>) attribute, joinType);
}
if(isList())
{
return from.join((ListAttribute<X, T>) attribute, joinType);
}
if(isCollection())
{
return from.join((CollectionAttribute<X, T>) attribute, joinType);
}
throw new ImpossibleException();
}
public Order buildOrder(CriteriaBuilderEx builder, From<?, ?> from, SortOrder direction)
{
if(direction == null)
{
return null;
}
Expression<?> expr = getExpression(builder, from);
return direction == SortOrder.ASCENDING ? builder.asc(expr) : builder.desc(expr);
}
}
with this construct, I can now safely:
public static List<Order> buildOrderList(CriteriaBuilderEx builder, From<?, ? extends Object> from, List<SortMeta> list)
{
return StreamEx.of(list)
.nonNull()
.map(x -> buildOrder(builder, from, x.getSortField(), x.getSortOrder()))
.nonNull()
.toList();
}
public static Order buildOrder(CriteriaBuilderEx builder, From<?, ? extends Object> from, String path, SortOrder direction)
{
if(path == null || path.isEmpty() || direction == null)
{
return null;
}
MetaDescriptor descriptor = MetaDescriptor.getDescriptor(from, path);
if(descriptor == null)
{
return null;
}
return descriptor.buildOrder(builder, from, direction);
}
If sortField exists only in one entity:
try{
path = root.get(sortField);
}catch (IllegalArgumentException e){
path = joinProject.get(sortField);
}
criteriaQuery.orderBy(cb.asc(path));
I'm trying to create a CQ5 service with a multifield configuration interface. It would be something like this but at the click of PLUS button it would add not just a new row but a group of N rows.
Property
Field1 +-
Field2
....
FieldN
Any advice?
As far as I know there is no such possibility in the Apache Felix.
Depending on your actual requirement I would consider decomposing the configuration. Try moving all the fieldsets (groups of fields that you'd like to add through the plus button) into a separated configuration. So, closely to the slf4j.Logger configuration you would have a Configuration Factory approach.
A simple configuration factory can look like following
#Component(immediate = true, configurationFactory = true, metatype = true, policy = ConfigurationPolicy.OPTIONAL, name = "com.foo.bar.MyConfigurationProvider", label = "Multiple Configuration Provider")
#Service(serviceFactory = false, value = { MyConfigurationProvider.class })
#Properties({
#Property(name = "propertyA", label = "Value for property A"),
#Property(name = "propertyB", label = "Value for property B") })
public class MyConfigurationProvider {
private String propertyA;
private String propertyB;
#Activate
protected void activate(final Map<String, Object> properties, final ComponentContext componentContext) {
propertyA = PropertiesUtil.toStringArray(properties.get("propertyA"), defaultValue);
propertyB = PropertiesUtil.toStringArray(properties.get("propertyB"), defaultValue);
}
}
Using it is as simple as adding a reference in any #Component
#Reference(cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, referenceInterface = MyConfigurationProvider.class, policy = ReferencePolicy.DYNAMIC)
private final List<MyConfigurationProvider> providers = new LinkedList<MyConfigurationProvider>();
protected void bindProviders(MyConfigurationProvider provider) {
providers.add(provider);
}
protected void unbindProviders(MyConfigurationProvider provider) {
providers.remove(provider);
}
This is one way of doing it.
#Component(label = "My Service", metatype = true, immediate = true)
#Service(MyService.class)
#Properties({
#Property(name = "my.property", description = "Provide details Eg: url=http://www.google.com|size=10|path=/content/project", value = "", unbounded = PropertyUnbounded.ARRAY) })
public class MyService {
private String[] myPropertyDetails;
#Activate
protected void activate(ComponentContext ctx) {
this.myPropertyDetails = getPropertyAsArray(ctx.getProperties().get("my.property"));
try {
if (null != myPropertyDetails && myPropertyDetails.length > 0) {
for(String myPropertyDetail : myPropertyDetails) {
Map<String, String> map = new HashMap<String, String>();
String[] propertyDetails = myPropertyDetails.split("|");
for (String keyValuePair : propertyDetails) {
String[] keyValue = keyValuePair.split("=");
if (null != keyValue && keyValue.length > 1) {
map.put(keyValue[0], keyValue[1]);
}
}
/* the map now has all the properties in the form of key value pairs for single field
use this for logic execution. when there are no multiple properties in the row,
you can skip the logic to split and add in the map */
}
}
} catch (Exception e) {
log.error( "Exception ", e.getMessage());
}
}
private String[] getPropertyAsArray(Object obj) {
String[] paths = { "" };
if (obj != null) {
if (obj instanceof String[]) {
paths = (String[]) obj;
} else {
paths = new String[1];
paths[0] = (String) obj;
}
}
return paths;
}
}
I'm new to android development and right now I'm trying to parse an J SON array into object and then save each object into a array list then display the array list with a array adapter. But, right now I can only get the array adapter to display the last element.
Here is the main class
public class Venue extends Activity {
Venue_Listing venue = new Venue_Listing();
ArrayList<Venue_Listing> venueList;
ListView listview;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listview = (ListView) findViewById(R.id.list);
new GetVenus().execute(URL);
venueList = new ArrayList<Venue_Listing>();
}
private class GetVenus extends AsyncTask<String, Void, Boolean> {
ProgressDialog dialog;
String data;
protected void onPreExecute() {
super.onPreExecute();
dialog = new ProgressDialog(Venue.this);
dialog.setMessage("Loading, please wait");
dialog.show();
dialog.setCancelable(false);
}
protected Boolean doInBackground(String... url) {
try {
HttpGet httppost = new HttpGet(url[0]);
HttpClient httpclient = new DefaultHttpClient();
HttpResponse response = httpclient.execute(httppost);
int status = response.getStatusLine().getStatusCode();
if (status == 200) {
HttpEntity entity = response.getEntity();
data = EntityUtils.toString(entity);
}
return true;
}
catch (ParseException e1) {
e1.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
return false;
}
protected void onPostExecute(Boolean result) {
dialog.cancel();
Venue_Listing_Adapter adapter = new
Venue_Listing_Adapter(getApplicationContext(),R.layout.venue_list_layout, venueList);
try {
JSONArray jarray = new JSONArray(data);
for (int i = 0; i < jarray.length(); i++) {
JSONObject object = jarray.getJSONObject(i);
if (venue != null) {
venue.SET_ID(object.getString("venue_ID"));
venue.SET_NAME(object.getString("name"));
listview.setAdapter(adapter);
}
}
}
catch (JSONException e) {
e.printStackTrace();
}
}
}
}
This class holds all the J SON parse data
public class Venue_Listing {
private String ID;
private String NAME;
public Venue_Listing() {
}
public Venue_Listing(String ID, String NAME) {
super();
this.ID = ID;
this.NAME = NAME;
}
// setter & getter....
And here is what I have for my array adapter
public class Venue_Listing_Adapter extends ArrayAdapter<Venue_Listing> {
private ArrayList<Venue_Listing> objects;
public Venue_Listing_Adapter(Context context, int resource, ArrayList<Venue_Listing> objects) {
super(context, resource, objects);
this.objects = objects;
}
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.venue_list_layout, null);
}
Venue_Listing i = objects.get(position);
if (i != null) {
TextView id = (TextView) v.findViewById(R.id.id);
TextView name = (TextView) v.findViewById(R.id.name);
if (id != null ){
id.setText(i.GET_ID());
}
if (name != null) {
name.setText(i.GET_NAME());
}
}
return v;
}
}
Any help would be appreciated
Thank you
Please print and check the values of 'venueList'.
You have not added values in 'venueList' object.
You need to create 'venue' object every time and add object 'venue' in 'venueList'. Hence you are not getting all items in list view.
The Code may go this way: -
Venue_Listing venue;
try {
JSONArray jarray = new JSONArray(data);
for (int i = 0; i < jarray.length(); i++) {
JSONObject object = jarray.getJSONObject(i);
venue = new Venue_Listing();
if (object != null) {
venue.SET_ID(object.getString("venue_ID"));
venue.SET_NAME(object.getString("name"));
venueList.add(venue);
}
}
Venue_Listing_Adapter adapter = new Venue_Listing_Adapter(getApplicationContext(),R.layout.venue_list_layout, venueList);
listview.setAdapter(adapter);
}
catch (JSONException e) {
e.printStackTrace();
}
Trying to remove or place items on a property grid by changing the Browsable attribute.
But unless browsable is set on object creation my code to change Browsable doesn't work. Now I can manually add browsable, but when I make a change to my entity (still developing project so lots of changes to entity) any additional attributes I add go away.
I attempted to set [Browsable(true)] two ways other ways: http://ardalis.com/adding-attributes-to-generated-classes and http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/617ebfca-0f68-4b90-83fd-0da758fadbd0/
Both seem to actually set the Browsable correctly, but when I loop thru the Attributes in Property Descriptor it is not there (for me to change).
String fieldname = "browsable"; // I also edit "description"
PropertyDescriptor pd = TypeDescriptor.GetProperties(o.GetType())[propertyName];
object attrib = null;
AttributeCollection attribs = pd.Attributes;
foreach (Attribute a in attribs)
{
if (a.GetType() == attributeType)
{
attrib = a;
break;
}
}
// The microsoft documentation leads one to believe the following line of code would find the desired attribute,
// negating the need for the more complete foreach statement above.
// However, it appears to find attribute even when it does not exist. Setting value for "found" attribute
// will result in random memory being changed, which results in very unpredictable behavior.
// attrib = pd.Attributes[t];
if (attrib != null)
{
// locate field that contains value
FieldInfo field = attrib.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
if (field != null)
{
if (field.FieldType == value.GetType())
{
// set field to desired value
field.SetValue(attrib, value);
}
}
}
else
{
throw new Exception("Attribute (" + attributeType.Name + ") does not exist for Property(" + propertyName + ")");
}
So I keep getting the Exception that I throw if it doesn't find "browsable" - but only if not set in Model.Designer.cs first.
Below is what my Model.Designer.cs looks like.
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=true)]
[DataMemberAttribute()]
[Browsable(false)] // this works, but goes away if change my entity
public Nullable<global::System.TimeSpan> SignoutAfter
{
get
{
return _SignoutAfter;
}
set
{
OnSignoutAfterChanging(value);
ReportPropertyChanging("SignoutAfter");
_SignoutAfter = StructuralObject.SetValidValue(value);
ReportPropertyChanged("SignoutAfter");
OnSignoutAfterChanged();
}
}
private Nullable<global::System.TimeSpan> _SignoutAfter;
partial void OnSignoutAfterChanging(Nullable<global::System.TimeSpan> value);
partial void OnSignoutAfterChanged();
So I need a way to either 1. add browsable to entity when I edit them so it is always on perhaps editing the t4, but I don't even know where to begin with that or 2. Another way to add or remove (and edit) the properties (see I might edit the description based on some logic) or 3 find the hole in my code so I can find and edit browsable (description and displayname).
Update The second link above, http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/617ebfca-0f68-4b90-83fd-0da758fadbd0/ , has a lot of what I need, I think. Adding a Attribute array variable to the class and some code to see if that is set seems to have the effect that I am looking for. But leaving this open to find a better answer.
partial class Client : ICustomTypeDescriptor
{
public Attribute[] SignOutAttributes; // added this
#region ICustomTypeDescriptor Members
... // see the link for the other code
public PropertyDescriptorCollection GetProperties (Attribute[] attributes)
{
var propsColl = TypeDescriptor.GetProperties (this, attributes, true);
var props = new List<PropertyDescriptor> ();
foreach (PropertyDescriptor prop in propsColl)
{
String strUPPERCaseName = prop.Name.ToUpper (); // for my thick fingers
// make sure case values are upper case
switch (strUPPERCaseName)
{
case "SIGNOUTAFTER":
if (SignOutAttributes != null)
{
props.Add(new CustomPropertyDescriptor(prop, SignOutAttributes));
}
else
{
props.Add (new CustomPropertyDescriptor (prop, new Attribute[]
{
new CategoryAttribute("Settings"),
new DisplayNameAttribute("Signout After"),
new BrowsableAttribute(true),
new ReadOnlyAttribute(false)
}));
}
break;
default:
props.Add (prop);
break;
}
}
return new PropertyDescriptorCollection (props.ToArray ());
}
In my code I can change the Attribute Array to have what Attribute values I want.
_client.SignOutAttributes = new Attribute[]
{
new CategoryAttribute ("My Category"),
new DisplayNameAttribute("Signout After"),
new BrowsableAttribute(true),
new ReadOnlyAttribute(false)
};
I'm not 100% happy with this. I have to write code for each Property.
Using ICustomTypeDescriptor is definitely the good solution when you want dynamic (set at runtime) properties. Here is generic ICustomTypeDescriptor utility class that I've been using for this sort of property grid hacking, it's pretty straightforward to use:
public sealed class DynamicTypeDescriptor: ICustomTypeDescriptor, INotifyPropertyChanged
{
private Type _type;
private AttributeCollection _attributes;
private TypeConverter _typeConverter;
private Dictionary<Type, object> _editors;
private EventDescriptor _defaultEvent;
private PropertyDescriptor _defaultProperty;
private EventDescriptorCollection _events;
public event PropertyChangedEventHandler PropertyChanged;
private DynamicTypeDescriptor()
{
}
public DynamicTypeDescriptor(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
_type = type;
_typeConverter = TypeDescriptor.GetConverter(type);
_defaultEvent = TypeDescriptor.GetDefaultEvent(type);
_defaultProperty = TypeDescriptor.GetDefaultProperty(type);
_events = TypeDescriptor.GetEvents(type);
List<PropertyDescriptor> normalProperties = new List<PropertyDescriptor>();
OriginalProperties = TypeDescriptor.GetProperties(type);
foreach (PropertyDescriptor property in OriginalProperties)
{
if (!property.IsBrowsable)
continue;
normalProperties.Add(property);
}
Properties = new PropertyDescriptorCollection(normalProperties.ToArray());
_attributes = TypeDescriptor.GetAttributes(type);
_editors = new Dictionary<Type, object>();
object editor = TypeDescriptor.GetEditor(type, typeof(UITypeEditor));
if (editor != null)
{
_editors.Add(typeof(UITypeEditor), editor);
}
editor = TypeDescriptor.GetEditor(type, typeof(ComponentEditor));
if (editor != null)
{
_editors.Add(typeof(ComponentEditor), editor);
}
editor = TypeDescriptor.GetEditor(type, typeof(InstanceCreationEditor));
if (editor != null)
{
_editors.Add(typeof(InstanceCreationEditor), editor);
}
}
public T GetPropertyValue<T>(string name, T defaultValue)
{
if (name == null)
throw new ArgumentNullException("name");
foreach (PropertyDescriptor pd in Properties)
{
if (pd.Name == name)
{
try
{
return (T)Convert.ChangeType(pd.GetValue(Component), typeof(T));
}
catch
{
return defaultValue;
}
}
}
return defaultValue;
}
public void SetPropertyValue(string name, object value)
{
if (name == null)
throw new ArgumentNullException("name");
foreach (PropertyDescriptor pd in Properties)
{
if (pd.Name == name)
{
pd.SetValue(Component, value);
break;
}
}
}
internal void OnValueChanged(PropertyDescriptor prop)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(prop.Name));
}
}
internal static T GetAttribute<T>(AttributeCollection attributes) where T : Attribute
{
if (attributes == null)
return null;
foreach (Attribute att in attributes)
{
if (typeof(T).IsAssignableFrom(att.GetType()))
return (T)att;
}
return null;
}
public sealed class DynamicProperty: PropertyDescriptor, INotifyPropertyChanged
{
private readonly Type _type;
private readonly bool _hasDefaultValue;
private readonly object _defaultValue;
private readonly PropertyDescriptor _existing;
private readonly DynamicTypeDescriptor _descriptor;
private Dictionary<Type, object> _editors;
private bool? _readOnly;
private bool? _browsable;
private string _displayName;
private string _description;
private string _category;
private List<Attribute> _attributes = new List<Attribute>();
public event PropertyChangedEventHandler PropertyChanged;
internal DynamicProperty(DynamicTypeDescriptor descriptor, Type type, object value, string name, Attribute[] attrs)
: base(name, attrs)
{
_descriptor = descriptor;
_type = type;
Value = value;
DefaultValueAttribute def = DynamicTypeDescriptor.GetAttribute<DefaultValueAttribute>(Attributes);
if (def == null)
{
_hasDefaultValue = false;
}
else
{
_hasDefaultValue = true;
_defaultValue = def.Value;
}
if (attrs != null)
{
foreach (Attribute att in attrs)
{
_attributes.Add(att);
}
}
}
internal static Attribute[] GetAttributes(PropertyDescriptor existing)
{
List<Attribute> atts = new List<Attribute>();
foreach (Attribute a in existing.Attributes)
{
atts.Add(a);
}
return atts.ToArray();
}
internal DynamicProperty(DynamicTypeDescriptor descriptor, PropertyDescriptor existing, object component)
: this(descriptor, existing.PropertyType, existing.GetValue(component), existing.Name, GetAttributes(existing))
{
_existing = existing;
}
public void RemoveAttributesOfType<T>() where T : Attribute
{
List<Attribute> remove = new List<Attribute>();
foreach (Attribute att in _attributes)
{
if (typeof(T).IsAssignableFrom(att.GetType()))
{
remove.Add(att);
}
}
foreach (Attribute att in remove)
{
_attributes.Remove(att);
}
}
public IList<Attribute> AttributesList
{
get
{
return _attributes;
}
}
public override AttributeCollection Attributes
{
get
{
return new AttributeCollection(_attributes.ToArray());
}
}
public object Value { get; set; }
public override bool CanResetValue(object component)
{
if (_existing != null)
return _existing.CanResetValue(component);
return _hasDefaultValue;
}
public override Type ComponentType
{
get
{
if (_existing != null)
return _existing.ComponentType;
return typeof(object);
}
}
public override object GetValue(object component)
{
if (_existing != null)
return _existing.GetValue(component);
return Value;
}
public override string Category
{
get
{
if (_category != null)
return _category;
return base.Category;
}
}
public void SetCategory(string category)
{
_category = category;
}
public override string Description
{
get
{
if (_description != null)
return _description;
return base.Description;
}
}
public void SetDescription(string description)
{
_description = description;
}
public override string DisplayName
{
get
{
if (_displayName != null)
return _displayName;
if (_existing != null)
return _existing.DisplayName;
return base.DisplayName;
}
}
public void SetDisplayName(string displayName)
{
_displayName = displayName;
}
public override bool IsBrowsable
{
get
{
if (_browsable.HasValue)
return _browsable.Value;
return base.IsBrowsable;
}
}
public void SetBrowsable(bool browsable)
{
_browsable = browsable;
}
public override bool IsReadOnly
{
get
{
if (_readOnly.HasValue)
return _readOnly.Value;
if (_existing != null)
return _existing.IsReadOnly;
ReadOnlyAttribute att = DynamicTypeDescriptor.GetAttribute<ReadOnlyAttribute>(Attributes);
if (att == null)
return false;
return att.IsReadOnly;
}
}
public void SetIsReadOnly(bool readOnly)
{
_readOnly = readOnly;
}
public override Type PropertyType
{
get
{
if (_existing != null)
return _existing.PropertyType;
return _type;
}
}
public override void ResetValue(object component)
{
if (_existing != null)
{
_existing.ResetValue(component);
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(Name));
}
_descriptor.OnValueChanged(this);
return;
}
if (CanResetValue(component))
{
Value = _defaultValue;
_descriptor.OnValueChanged(this);
}
}
public override void SetValue(object component, object value)
{
if (_existing != null)
{
_existing.SetValue(component, value);
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(Name));
}
_descriptor.OnValueChanged(this);
return;
}
Value = value;
_descriptor.OnValueChanged(this);
}
public override bool ShouldSerializeValue(object component)
{
if (_existing != null)
return _existing.ShouldSerializeValue(component);
return false;
}
public override object GetEditor(Type editorBaseType)
{
if (editorBaseType == null)
throw new ArgumentNullException("editorBaseType");
if (_editors != null)
{
object type;
if ((_editors.TryGetValue(editorBaseType, out type)) && (type != null))
return type;
}
return base.GetEditor(editorBaseType);
}
public void SetEditor(Type editorBaseType, object obj)
{
if (editorBaseType == null)
throw new ArgumentNullException("editorBaseType");
if (_editors == null)
{
if (obj == null)
return;
_editors = new Dictionary<Type, object>();
}
if (obj == null)
{
_editors.Remove(editorBaseType);
}
else
{
_editors[editorBaseType] = obj;
}
}
}
public PropertyDescriptor AddProperty(Type type, string name, object value, string displayName, string description, string category, bool hasDefaultValue, object defaultValue, bool readOnly)
{
return AddProperty(type, name, value, displayName, description, category, hasDefaultValue, defaultValue, readOnly, null);
}
public PropertyDescriptor AddProperty(
Type type,
string name,
object value,
string displayName,
string description,
string category,
bool hasDefaultValue,
object defaultValue,
bool readOnly,
Type uiTypeEditor)
{
if (type == null)
throw new ArgumentNullException("type");
if (name == null)
throw new ArgumentNullException("name");
List<Attribute> atts = new List<Attribute>();
if (!string.IsNullOrEmpty(displayName))
{
atts.Add(new DisplayNameAttribute(displayName));
}
if (!string.IsNullOrEmpty(description))
{
atts.Add(new DescriptionAttribute(description));
}
if (!string.IsNullOrEmpty(category))
{
atts.Add(new CategoryAttribute(category));
}
if (hasDefaultValue)
{
atts.Add(new DefaultValueAttribute(defaultValue));
}
if (uiTypeEditor != null)
{
atts.Add(new EditorAttribute(uiTypeEditor, typeof(UITypeEditor)));
}
if (readOnly)
{
atts.Add(new ReadOnlyAttribute(true));
}
DynamicProperty property = new DynamicProperty(this, type, value, name, atts.ToArray());
AddProperty(property);
return property;
}
public void RemoveProperty(string name)
{
if (name == null)
throw new ArgumentNullException("name");
List<PropertyDescriptor> remove = new List<PropertyDescriptor>();
foreach (PropertyDescriptor pd in Properties)
{
if (pd.Name == name)
{
remove.Add(pd);
}
}
foreach (PropertyDescriptor pd in remove)
{
Properties.Remove(pd);
}
}
public void AddProperty(PropertyDescriptor property)
{
if (property == null)
throw new ArgumentNullException("property");
Properties.Add(property);
}
public override string ToString()
{
return base.ToString() + " (" + Component + ")";
}
public PropertyDescriptorCollection OriginalProperties { get; private set; }
public PropertyDescriptorCollection Properties { get; private set; }
public DynamicTypeDescriptor FromComponent(object component)
{
if (component == null)
throw new ArgumentNullException("component");
if (!_type.IsAssignableFrom(component.GetType()))
throw new ArgumentException(null, "component");
DynamicTypeDescriptor desc = new DynamicTypeDescriptor();
desc._type = _type;
desc.Component = component;
// shallow copy on purpose
desc._typeConverter = _typeConverter;
desc._editors = _editors;
desc._defaultEvent = _defaultEvent;
desc._defaultProperty = _defaultProperty;
desc._attributes = _attributes;
desc._events = _events;
desc.OriginalProperties = OriginalProperties;
// track values
List<PropertyDescriptor> properties = new List<PropertyDescriptor>();
foreach (PropertyDescriptor pd in Properties)
{
DynamicProperty ap = new DynamicProperty(desc, pd, component);
properties.Add(ap);
}
desc.Properties = new PropertyDescriptorCollection(properties.ToArray());
return desc;
}
public object Component { get; private set; }
public string ClassName { get; set; }
public string ComponentName { get; set; }
AttributeCollection ICustomTypeDescriptor.GetAttributes()
{
return _attributes;
}
string ICustomTypeDescriptor.GetClassName()
{
if (ClassName != null)
return ClassName;
if (Component != null)
return Component.GetType().Name;
if (_type != null)
return _type.Name;
return null;
}
string ICustomTypeDescriptor.GetComponentName()
{
if (ComponentName != null)
return ComponentName;
return Component != null ? Component.ToString() : null;
}
TypeConverter ICustomTypeDescriptor.GetConverter()
{
return _typeConverter;
}
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
{
return _defaultEvent;
}
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
{
return _defaultProperty;
}
object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
{
object editor;
if (_editors.TryGetValue(editorBaseType, out editor))
return editor;
return null;
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
{
return _events;
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
{
return _events;
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
{
return Properties;
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
return Properties;
}
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
{
return Component;
}
}