Пример #1
0
//
// Everything inside of a ( ) resolves to a DependencyProperty, if there is a
// '.' after the property, we get the object, and continue resolving from there
// if there is a [n] after the property, we convert the property to a collection
// and grab the nth item.
//
// Dependency properties can be specified as (PropertyName) of the current object
// or they can be specified as (DependencyObject.PropertyName).
//
// Returns NULL on any error
//
DependencyProperty *
resolve_property_path (DependencyObject **o, PropertyPath *propertypath, GHashTable *promoted_values)
{
	g_return_val_if_fail (o != NULL, NULL);
	g_return_val_if_fail (propertypath != NULL, NULL);
	g_return_val_if_fail (propertypath->path != NULL || propertypath->property != NULL, NULL);
	
	if (propertypath->property)
		return propertypath->property;

	const char *path = propertypath->path;
	if (propertypath->expanded_path)
		path = propertypath->expanded_path;

	const char *inend = path + strlen (path);
	register const char *inptr = path;
	const char *start, *prop = path;
	bool expression_found = false;
	DependencyProperty *res = NULL;
	DependencyObject *lu = *o;
	Collection *collection;
	char *p, *name = NULL;
	Value *value = NULL;
	Type *type = NULL;
	int index;
	bool paren_open = false;
	bool tick_open = false;
	bool cloned = false;

	while (inptr < inend) {
		switch (*inptr++) {
		case '(':
			paren_open = true;
			break;
		case ')':
			paren_open = false;
			break;
		case '\'':
			// Ticks are only legal in expanded paths, so we should just fail here
			if (!propertypath->expanded_path) {
				g_warning ("The ' character is not legal in property paths.");
				break;
			}

			tick_open = !tick_open;
			break;
		case '.':
			if (tick_open)
				continue;

			// resolve the dependency property
			if (res) {
				DependencyObject *new_lu;

				// make sure that we are getting what we expect
				if (!(value = lu->GetValue (res)))
					goto error;

				if (!(new_lu = value->AsDependencyObject ()))
					goto error;

				if (!cloned && !g_hash_table_lookup (promoted_values, value) && !value->Is (lu->GetDeployment (), Type::UIELEMENT)) {
					// we need to clone the value here so that we deep copy any
					// DO subclasses (such as brushes, etc) that we're promoting
					// from a shared space (Styles, default values)
					Value *cloned_value = Value::Clone (value);
					
					DependencyObject *cloned_do = cloned_value->AsDependencyObject();
					if (cloned_do != NULL) {
						new_lu = cloned_do;
						lu->SetValue (res, cloned_value);
						delete cloned_value;
						
						cloned_value = lu->GetValue (res);
						g_hash_table_insert (promoted_values, cloned_value, cloned_value);
					}
				}

				lu = new_lu;
			}
			
			expression_found = false;
			prop = inptr;
			break;
		case '[':
			// Need to be a little more loving
			if (*inptr == '\0')
				break;
			
			index = strtol (inptr, &p, 10);
			if (*p != ']' || *(p + 1) != '.')
				break;
			
			inptr = p + 2;
			prop = inptr;

			if (expression_found) {
				expression_found = false;
				if (!(value = lu->GetValue (res)))
					goto error;
			}
			
			if (value == NULL)
				goto error;
			
			if (!(collection = value->AsCollection ()))
				goto error;

			if (!(value = collection->GetValueAt (index)))
				goto error;
			
			if (!(lu = value->AsDependencyObject ()))
				goto error;
			
			break;
		
		default:
			bool explicit_type = false;
			expression_found = true;
			start = inptr - 1;

			while (inptr < inend && (*inptr != '.' || tick_open) && (!paren_open || *inptr != ')') && *inptr != '[') {
				if (*inptr == '\'') {
					tick_open = !tick_open;
					if (!tick_open) {
						inptr++;
						break;
					}
				}
				inptr++;
			}

			if (inptr == start)
				goto error;

			if (*inptr == '.') {
				// we found a type name, now we need to find the property name
				if ((inptr - start) == 11 && !g_ascii_strncasecmp (start, "TextElement", 11)) {
					// Some Beta versions of Blend had a bug where they would save the TextBlock
					// properties as TextElement instead. Since Silverlight 1.0 works around this
					// bug, we should too. Fixes http://silverlight.timovil.com and
					// http://election.msn.com/podium08.aspx.
					type = Type::Find (lu->GetDeployment (), "TextBlock");
					explicit_type = true;
				} else {
					const char *s = inptr;
					if (*(inptr -1) == '\'' && !tick_open) {
						s = inptr - 1;
					}
					name = g_strndup (start, s - start);
					type = lookup_type (lu, name);
					explicit_type = true;
					if (!type)
						type = lu->GetType ();
					g_free (name);
				}
				
				inptr++;
				start = inptr;
				while (inptr < inend && (!paren_open || *inptr != ')') && (*inptr != '.' || tick_open)) {
					if (*inptr == '\'') {
						tick_open = !tick_open;
						if (!tick_open) {
							inptr++;
							break;
						}
					}
					inptr++;
				}
				
				if (inptr == start)
					goto error;
			} else {
				type = lu->GetType ();
				explicit_type = false;
			}
			
			if ((*inptr != ')' && paren_open) || !type)
				goto error;

			name = g_strndup (start, inptr - start);
			if (!(res = DependencyProperty::GetDependencyProperty (type, name)) && lu)
				res = DependencyProperty::GetDependencyProperty (lu->GetType (), name);

			if (!res) {
				g_free (name);
				goto error;
			}

			if (!res->IsAttached () && !lu->Is (type->GetKind ())) {
				// We try to be gracefull here and do something smart...
				if (!(res = DependencyProperty::GetDependencyProperty (lu->GetType (), name))) {
					g_free (name);
					goto error;
				}
			}
			
			if (res->IsAttached () && explicit_type && !paren_open)
				goto error;
			
			g_free (name);
			break;
		}
	}
	
	*o = lu;
	return res;
	
 error:
	*o = NULL;	
	return NULL;
}