incubator-xap-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jmarga...@apache.org
Subject svn commit: r482095 - in /incubator/xap/trunk/src/xap: data/ data/controller/ data/datasource/ resolver/ session/ taghandling/
Date Mon, 04 Dec 2006 07:30:49 GMT
Author: jmargaris
Date: Sun Dec  3 23:30:46 2006
New Revision: 482095

URL: http://svn.apache.org/viewvc?view=rev&rev=482095
Log:
Asynchronous free binding now working

Added:
    incubator/xap/trunk/src/xap/data/controller/AttributeValueLocation.js
    incubator/xap/trunk/src/xap/resolver/ResolutionHandler.js
      - copied, changed from r481114, incubator/xap/trunk/src/xap/resolver/AttributeResolutionHandler.js
    incubator/xap/trunk/src/xap/resolver/ResolutionInfo.js   (with props)
Removed:
    incubator/xap/trunk/src/xap/resolver/AttributeResolutionHandler.js
Modified:
    incubator/xap/trunk/src/xap/data/DataFramework.js
    incubator/xap/trunk/src/xap/data/controller/Binding.js
    incubator/xap/trunk/src/xap/data/controller/XmlLocation.js
    incubator/xap/trunk/src/xap/data/datasource/AbstractDataSource.js
    incubator/xap/trunk/src/xap/data/datasource/SimpleDocumentDataSource.js
    incubator/xap/trunk/src/xap/session/ClientSession.js
    incubator/xap/trunk/src/xap/taghandling/AbstractTagImpl.js

Modified: incubator/xap/trunk/src/xap/data/DataFramework.js
URL: http://svn.apache.org/viewvc/incubator/xap/trunk/src/xap/data/DataFramework.js?view=diff&rev=482095&r1=482094&r2=482095
==============================================================================
--- incubator/xap/trunk/src/xap/data/DataFramework.js (original)
+++ incubator/xap/trunk/src/xap/data/DataFramework.js Sun Dec  3 23:30:46 2006
@@ -17,29 +17,19 @@
  
 Xap.require("xap.session.ClientEvent");
 Xap.require("xap.session.ClientSession"); 
-//Xap.require("xap.mco.AbstractMco") ; 
 Xap.require("xap.mco.ContainerLifeCycleObject");
 Xap.require("xap.session.DeclarativeArgumentParser"); 
-//Xap.require("xap.resolver.ResolutionInfo") ;
 Xap.require("xap.data.controller.Binding") ;
-
 Xap.require("xap.session.Container") ;
-// Xap.require("xap.data.controller.BindingResolver") ;
 Xap.require("xap.data.controller.BindingType") ;
-// Xap.require("xap.data.controller.DataControllerException") ;
-// Xap.require("xap.data.controller.IteratorContainer") ; 
-// Xap.require("xap.data.controller.IteratorContainerImpl") ;
-// Xap.require("xap.data.datasource.DataAccessException") ;
-// Xap.require("xap.data.datasource.DataSourceContainer") ;
 Xap.require("xap.data.datasource.DataSourceContainerImpl") ;
-// Xap.require("xap.data.datasource.MalformedQueryException") ;
-// Xap.require("xap.data.datasource.UnsupportedBindingTypeException") ;
 Xap.require("xap.data.formatter.FormatterContainerImpl") ;
 Xap.require("xap.util.EscapeSyntaxException") ; 
+Xap.require("xap.resolver.ResolutionInfo");
 
 /**
- * This class encapsulates all the data primitives available for a client session.
- * It may be used to lookup bindings, formatters, data sources, and validators.
+ * Thisclass encapsulates all the data primitives available for a client session.
+ * It may be used to lookup bindings, formatters, data source, and validators.
  *  
  * @author dgennaco
  */
@@ -178,21 +168,9 @@
 								xap.data.controller.BindingType.ONE_TIME, 
 								null
 									);
-	return this.bind(binding);
+	return this.bindBinding(binding);
 };
 
-
-// Original overloaded method for single-argument case:
-//
-// * @public 
-// * @return {Object}
-// * @param select{String} A selection string.
-// */
-//xap.data.DataFramework.prototype.relativeBind = function (select) {
-//	return this.relativeBind(null, select);
-//};
-
-
 /*public void*/
 xap.data.DataFramework.prototype.unload = function () {
 	this._bindingContainer.destroy();
@@ -255,56 +233,35 @@
 	this._dataSourceContainer = aContainer;
 };
 
+xap.data.DataFramework.prototype.bind = function( stringOrBinding){
+	if ( typeof bindingOrString == "string"){
+		var binding =  this._bindingResolver.createBindingFromParameterString(bindingString);
+		return this.bindBinding(binding);	
+	}
+	else{
+		return this.bindBinding(stringOrBinding);	
+	}
+}
 
-/**
- * Bind using the specified bind string, this string is
- * a semicolon delimited list of name value pairs where the
- * name and value are separated by equals.
- * 
- * @param bindString the bind string
- * 
- * @return the value retrieved from the binding.
- * 
- * @throws DataAccessException
- * @throws MalformedQueryException
- * @throws UnsupportedBindingTypeException
- * @throws EscapeSyntaxException
- * @throws DataControllerException
- *
- * @public
- * @return {Object}
- * @param binding{Binding}, or
- *
- * @param bindString{String}
- **/
-// Implementor of DataService interface: xap.data.DataFramework.prototype.bind = function
(bindingOrString) {
-xap.data.DataFramework.prototype.bind = function (bindingOrString) {
-	// Need to handle string or binding cases...subclass?
-	if ( typeof bindingOrString != "string"){
-	
-		return bindingOrString.resolve();
-	
-//		/*ClientEvent*/
-//		var event =  this._session.getEventHandler().getClientEvent();
-//		/*ResolutionInfo*/
-//		var info =  event.getAttribute("resolverInfo");
-//
-// Leave ResolutionInfo for later	
-//		if (info==null){
-//			return binding.resolve();
-//		}
-//		else if (info.getResolutionType()==ResolutionInfo.WHOLE_ATTRIBUTE){
-//			return binding.resolve(info.getSourceElement(), info.getAttributeName());
-//		}
-//		return binding.resolve(info.getSourceElement(),info.getTextNode(),info.getNodeIndex());

-	
-	} else {
-		var binding 
-			=  this._bindingResolver.createBindingFromParameterString(bindingOrString);
-		return this.bind(binding);	
-	}	
+xap.data.DataFramework.prototype.bindBinding = function ( binding ) {
+		/*ClientEvent*/
+		var event =  this._session.getResolutionHandler()._clientEvent;
+		
+		/*ResolutionInfo*/
+		var info =  event._resolverInfo;
+		
+		if (!info){
+			return binding.resolve();
+		}
+		else if (info._resolutionType==xap.resolver.ResolutionInfo.WHOLE_ATTRIBUTE){	
+			return binding.resolveAttribute(info._sourceElement, info._attributeName);
+		}
+		return binding.resolve();
+		
+		//IMPORTANT binding a text node needs to work!
+	//	return binding.resolve(info.getSourceElement(),info.getTextNode(),info.getNodeIndex());

+}
 
-};
 
 
 

Added: incubator/xap/trunk/src/xap/data/controller/AttributeValueLocation.js
URL: http://svn.apache.org/viewvc/incubator/xap/trunk/src/xap/data/controller/AttributeValueLocation.js?view=auto&rev=482095
==============================================================================
--- incubator/xap/trunk/src/xap/data/controller/AttributeValueLocation.js (added)
+++ incubator/xap/trunk/src/xap/data/controller/AttributeValueLocation.js Sun Dec  3 23:30:46
2006
@@ -0,0 +1,49 @@
+/*
+ * Copyright  2006 The Apache Software Foundation.
+ *
+ *  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.
+ *
+ */
+
+Xap.provide("xap.data.controller.AttributeValueLocation");
+
+Xap.require("xap.data.controller.XmlLocation");
+Xap.require("xap.xml.dom.Document");
+Xap.require("xap.xml.dom.XapElement");
+
+
+/**
+ * @fileoverview A generic XML value location.
+ * 
+ * @author dgennaco
+ * @constructor
+ * @param element{XapElement}
+**/
+xap.data.controller.AttributeValueLocation = function(element, attName) {
+	xap.data.controller.XmlLocation.call(this,element);
+	this._attributeName = attName;
+
+};
+
+Xap.setupClassAsSubclassOf("xap.data.controller.AttributeValueLocation", "xap.data.controller.XmlLocation");
+
+xap.data.controller.AttributeValueLocation.prototype.setData = function(data){
+	if (this._attributeName) {
+		this._element.setAttribute(this._attributeName, data);
+	}
+}
+
+xap.data.controller.AttributeValueLocation.prototype.remove = function(){
+	this._element.removeAttribute(_attributeName);
+	xap.data.controller.XmlLocation.remove.call(this);
+}	
\ No newline at end of file

Modified: incubator/xap/trunk/src/xap/data/controller/Binding.js
URL: http://svn.apache.org/viewvc/incubator/xap/trunk/src/xap/data/controller/Binding.js?view=diff&rev=482095&r1=482094&r2=482095
==============================================================================
--- incubator/xap/trunk/src/xap/data/controller/Binding.js (original)
+++ incubator/xap/trunk/src/xap/data/controller/Binding.js Sun Dec  3 23:30:46 2006
@@ -22,6 +22,7 @@
 Xap.require("xap.data.DataServiceFactory");
 Xap.require("xap.data.controller.BindingType");
 Xap.require("xap.util.Vector");
+Xap.require("xap.data.controller.AttributeValueLocation");
 
 
 /*
@@ -86,16 +87,6 @@
 	binding._currentValue = binding._defaultValue;
 	return binding;
 }
- 
- 
-/**
- * Get the client session for this binding.
- * @public
- * @return {ClientSession} The session.
- */
-xap.data.controller.Binding.prototype.getSession = function () {
-	return this._session;
-};
 
 
 /**
@@ -138,9 +129,13 @@
 	// (indicated by the current value being the default value,
 	// we need to add a bind target so that the data can be set
 	// at a later time 
-	if ((this._bindingType != xap.data.controller.BindingType.ONE_TIME) || ((this._bindingType
== xap.data.controller.BindingType.ONE_TIME) && (this._currentValue === this._defaultValue)))
{
-		//IMPORTANT this needs to work!
-		//this.addBindTarget(new AttributeValueLocation(e, attributeName));
+	
+	//IMPORTANT this === is kind of stupid in that the default could be ""
+	//and we could get back "" and consider those the same
+	//we really ONLY want to do this if no data is available yet
+	//in the case of one_time binding
+	if ((this._bindingType != xap.data.controller.BindingType.ONE_TIME) || (this._currentValue
=== this._defaultValue)) {
+		this.addBindTarget(new xap.data.controller.AttributeValueLocation(e, attributeName));
 	}
 	return this._currentValue;
 };
@@ -309,8 +304,6 @@
 		}
 	}
 	
-	//IMPORTANT if this was an asynch call we now need to stick this value into the UI!
-	//this is the right code (??) but setData doesn't work right now
 	this.setData();
 	
 	

Modified: incubator/xap/trunk/src/xap/data/controller/XmlLocation.js
URL: http://svn.apache.org/viewvc/incubator/xap/trunk/src/xap/data/controller/XmlLocation.js?view=diff&rev=482095&r1=482094&r2=482095
==============================================================================
--- incubator/xap/trunk/src/xap/data/controller/XmlLocation.js (original)
+++ incubator/xap/trunk/src/xap/data/controller/XmlLocation.js Sun Dec  3 23:30:46 2006
@@ -33,26 +33,13 @@
 	// Need (in essence) a no-argument constructor
 	// so that one of these can be used as a prototype
 	// object, hence the test:
-	if( 
-		(typeof element != "undefined")
-			&&	(element != null) 
-		){	
+	
+	if (element){
 		this._element.addDocumentOwnershipListener(this);
 	}		
 };
+
 
-Xap.setupClassAsSubclassOf("xap.data.controller.XmlLocation", "Object");
-/**
- * @protected {XapElement}
-**/
-xap.data.controller.XmlLocation.prototype._element = null;
-/**
- * @private {LocationListener}
-**/
-xap.data.controller.XmlLocation.prototype._locationListener = null;
-/*
- * DocumentOwnershipListener interface methods
- */
 /**
  * @public
  * @return {void}
@@ -94,25 +81,19 @@
 xap.data.controller.XmlLocation.prototype.setLocationListener = function (listener) {
 	this._locationListener = listener;
 };
-/*
- * END - XML Location Interface methods
- */
-/**
- * @public
- * @return {String}
-**/
-xap.data.controller.XmlLocation.prototype.toString = function () {
-	return (this.superclass.toString.call(this) + "-->" + this._element.toString());
-};
+
+
 /**
  * @protected
  * @return {void}
 **/
-xap.data.controller.XmlLocation.prototype.invalidate = function () {
-	if (this._locationListener != null) {
+xap.data.controller.XmlLocation.prototype.invalidate = function () {
+	
+	//TODO make sure this cleanup is working
+	if (this._locationListener ) {
 		this._locationListener.locationInvalidated(this);
 			// Since we are now invalid, clear the location listener.
-		this._locationListener = null;
+		delete this._locationListener;
 	}
 	this._element.removeDocumentOwnershipListener(this);
 };

Modified: incubator/xap/trunk/src/xap/data/datasource/AbstractDataSource.js
URL: http://svn.apache.org/viewvc/incubator/xap/trunk/src/xap/data/datasource/AbstractDataSource.js?view=diff&rev=482095&r1=482094&r2=482095
==============================================================================
--- incubator/xap/trunk/src/xap/data/datasource/AbstractDataSource.js (original)
+++ incubator/xap/trunk/src/xap/data/datasource/AbstractDataSource.js Sun Dec  3 23:30:46
2006
@@ -502,6 +502,7 @@
  **/
 xap.data.datasource.AbstractDataSource.prototype.handleQuery = function (query, queryContext,
listener) {
 	if (listener.isDataRetrievalListener()) {
+		
 		this.handleDataQuery(query, queryContext, listener);
 	} else {
 		if (listener.isDataSetRetrievalListener()) {	

Modified: incubator/xap/trunk/src/xap/data/datasource/SimpleDocumentDataSource.js
URL: http://svn.apache.org/viewvc/incubator/xap/trunk/src/xap/data/datasource/SimpleDocumentDataSource.js?view=diff&rev=482095&r1=482094&r2=482095
==============================================================================
--- incubator/xap/trunk/src/xap/data/datasource/SimpleDocumentDataSource.js (original)
+++ incubator/xap/trunk/src/xap/data/datasource/SimpleDocumentDataSource.js Sun Dec  3 23:30:46
2006
@@ -133,8 +133,22 @@
 		var exprContext =   new google.ExprContext(contextObject) ;
 		var parsedXPathExpr = google.xpathParse(theQuery) ;
 		var targetNodes = parsedXPathExpr.evaluate( exprContext );
-		theData = targetNodes.nodeSetValue() ; // //array of dom nodes
+		
+		if (targetNodes.type=='node-set'){
+			theData = targetNodes.nodeSetValue();
+		}
+		else if (targetNodes.type=='string'){
+			theData = targetNodes.stringValue();
+		}
+		else if (targetNodes.type=='number'){
+			theData = targetNodes.numberValue();
+		}
+		else if (targetNodes.type=='boolean'){
+			theData = targetNodes.booleanValue();
+		}
 	} catch(ex){
+		//TODO this error is not being reported well,
+		//you can force it by calling nodeSetValue() on a string
 		throw new xap.util.Exception(
 				"SimpleDocumentDataSource.executeQuery::source: '"
 										+ contextObject
@@ -143,5 +157,8 @@
 										+ex
 										) ;					
 	}
+	
+	//IMPORTANT make everyone aware of the fact this can return a raw string,
+	//a boolean, a number or an array of nodes
 	return theData ;
 }

Copied: incubator/xap/trunk/src/xap/resolver/ResolutionHandler.js (from r481114, incubator/xap/trunk/src/xap/resolver/AttributeResolutionHandler.js)
URL: http://svn.apache.org/viewvc/incubator/xap/trunk/src/xap/resolver/ResolutionHandler.js?view=diff&rev=482095&p1=incubator/xap/trunk/src/xap/resolver/AttributeResolutionHandler.js&r1=481114&p2=incubator/xap/trunk/src/xap/resolver/ResolutionHandler.js&r2=482095
==============================================================================
--- incubator/xap/trunk/src/xap/resolver/AttributeResolutionHandler.js (original)
+++ incubator/xap/trunk/src/xap/resolver/ResolutionHandler.js Sun Dec  3 23:30:46 2006
@@ -15,40 +15,45 @@
  *
  */
 
-//Let Dojo know what to expect from this file:
-Xap.provide("xap.resolver.AttributeResolutionHandler"); 
+Xap.provide("xap.resolver.ResolutionHandler"); 
 
 Xap.require("xap.session.ClientEvent");
 Xap.require("xap.session.EventHandler") ;
+Xap.require("xap.resolver.ResolutionInfo");
+Xap.require("xap.util.TypeUtils");
 
 /**
  * @param session{xap.session.ClientSession}
  * @constructor
  *
 **/ 
-xap.resolver.AttributeResolutionHandler = function(session){		
+xap.resolver.ResolutionHandler = function(session){		
 	this._session = session ;
 }
 
+//TODO resolveTextNode
 
-Xap.setupClassAsSubclassOf("xap.resolver.AttributeResolutionHandler","Object") ;
-
-
-
-xap.resolver.AttributeResolutionHandler.prototype.resolveAttribute = function(event,xapElement){
+xap.resolver.ResolutionHandler.prototype.resolveAttribute = function(event,xapElement){
 	
 	var attributeValue=event.getNewValue() ;
 	
-	if ( !this.mightNeedResolution(attributeValue) ){
-		return ;
-	}
-
+	//TODO move the escaping syntax stuff to here instead of testing 
+	//in AbstractTagImpl?
+	
 	// If we've reached this point, we've got brackets
 	// fore and aft, so remove them:
 	attributeValue = attributeValue.substring(1,attributeValue.length-1) ;
 
-	// Lifting code from AbstractTagImpl.fireEvent() 
-	// and EventHandler.fireEvent():
+	var info = xap.resolver.ResolutionInfo.createWholeAttributeReplacement(xapElement,event.getName());
+	var clientEvent = new xap.session.ClientEvent(xapElement,this._session);
+	
+	
+	clientEvent._resolverInfo = info;
+	
+	
+	//save this so we can look it up later
+	this._clientEvent = clientEvent;
+	
 	var handler = this._session.getEventHandler() ;
 
 	try{
@@ -56,27 +61,24 @@
 		attributeValue = attributeValue.replace(/\\}/g,"}") ;
 		//Compactor needs this l-bracket escaped
 		attributeValue = attributeValue.replace(/\\\{/g,"{") ; 
-
-		var clientEvent = new xap.session.ClientEvent( 
-													xapElement, 
-													this._session
-														);	
-
 			
 		// we use parseArgument here because technically an object 
 		// event is just an argument as they can be imbedded in other 
 		// arguments etc  TODO: understand this
 		var parser = this._session.getDeclarativeArgumentParser() ;
+		
 		var parseResult = parser.parseArgument(
 												attributeValue,
 												0,
 												xapElement, 
 												clientEvent
-												);		
-
-		if( typeof parseResult._resultObject != "undefined"){
-			event.setNewValue(parseResult._resultObject);		
-		}
+												);
+		delete this._clientEvent;
+												
+		if (!parseResult._resultObject){
+			return "";
+		}			
+		return xap.util.TypeUtils.convertToString(parseResult._resultObject);
 	} catch(anException){
 		this._session.handleException(anException);
 	}
@@ -84,7 +86,7 @@
 
 
 
-xap.resolver.AttributeResolutionHandler.prototype.mightNeedResolution =function(attributeValue){

+xap.resolver.ResolutionHandler.prototype.needsResolving =function(attributeValue){ 
 	return !( attributeValue == null 
 				// Must have at least two brackets:
 				|| attributeValue.length < 2

Added: incubator/xap/trunk/src/xap/resolver/ResolutionInfo.js
URL: http://svn.apache.org/viewvc/incubator/xap/trunk/src/xap/resolver/ResolutionInfo.js?view=auto&rev=482095
==============================================================================
--- incubator/xap/trunk/src/xap/resolver/ResolutionInfo.js (added)
+++ incubator/xap/trunk/src/xap/resolver/ResolutionInfo.js Sun Dec  3 23:30:46 2006
@@ -0,0 +1,17 @@
+Xap.provide("xap.resolver.ResolutionInfo"); 
+
+
+xap.resolver.ResolutionInfo = function(){}
+
+xap.resolver.ResolutionInfo.WHOLE_ATTRIBUTE = 1;
+xap.resolver.ResolutionInfo.WHOLE_TEXT_NODE = 2;
+
+xap.resolver.ResolutionInfo.createWholeAttributeReplacement = function( sourceElement, attributeName){
+	var info = new xap.resolver.ResolutionInfo();
+	info._resolutionType = xap.resolver.ResolutionInfo.WHOLE_ATTRIBUTE;
+    info._attributeName = attributeName;
+    info._sourceElement = sourceElement;
+    return info;
+} 
+
+//TODO text node/child resolution
\ No newline at end of file

Propchange: incubator/xap/trunk/src/xap/resolver/ResolutionInfo.js
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/xap/trunk/src/xap/session/ClientSession.js
URL: http://svn.apache.org/viewvc/incubator/xap/trunk/src/xap/session/ClientSession.js?view=diff&rev=482095&r1=482094&r2=482095
==============================================================================
--- incubator/xap/trunk/src/xap/session/ClientSession.js (original)
+++ incubator/xap/trunk/src/xap/session/ClientSession.js Sun Dec  3 23:30:46 2006
@@ -24,7 +24,7 @@
 Xap.require("xap.util.Hashtable");
 Xap.require("xap.taghandling.PluginRegistryImpl");
 Xap.require("xap.taghandling.PluginDocumentHandler");
-Xap.require("xap.resolver.AttributeResolutionHandler");
+Xap.require("xap.resolver.ResolutionHandler");
 Xap.require("xap.taghandling.AbstractTagImpl");
 Xap.require("xap.session.EventHandler");
 Xap.require("xap.session.DeclarativeArgumentParser");
@@ -71,7 +71,7 @@
 	
 	this._namespaceHandlerManager = new xap.xml.NamespaceHandlerManager( this );
 	
-	this._attributeResolver = new xap.resolver.AttributeResolutionHandler( this ) ;
+	this._resolutionHandler = new xap.resolver.ResolutionHandler( this ) ;
 	
 	//set up a plugin document handler for the UI document
 	
@@ -96,8 +96,8 @@
 	return this._namespaceHandlerManager;
 }
 
-xap.session.ClientSession.prototype.getAttributeResolver = function() {
-	return this._attributeResolver;
+xap.session.ClientSession.prototype.getResolutionHandler = function() {
+	return this._resolutionHandler;
 }
 
 xap.session.ClientSession.prototype.getApplication = function() {

Modified: incubator/xap/trunk/src/xap/taghandling/AbstractTagImpl.js
URL: http://svn.apache.org/viewvc/incubator/xap/trunk/src/xap/taghandling/AbstractTagImpl.js?view=diff&rev=482095&r1=482094&r2=482095
==============================================================================
--- incubator/xap/trunk/src/xap/taghandling/AbstractTagImpl.js (original)
+++ incubator/xap/trunk/src/xap/taghandling/AbstractTagImpl.js Sun Dec  3 23:30:46 2006
@@ -679,8 +679,16 @@
  * @throw exception if resolver has a problem parsing the new event's value 
  */
 xap.taghandling.AbstractTagImpl.prototype.beforeAttributeSet = function( event ){	
-	var resolver  = this.getSession().getAttributeResolver() ;
-	resolver.resolveAttribute(event,this.getElement())
+	var resolver  = this.getSession().getResolutionHandler() ;
+	
+	if (resolver.needsResolving(event.getNewValue())){
+		event.setNewValue(resolver.resolveAttribute(event,this.getElement()));
+	}
+	else{
+		//if we didn't need resolving we might need to unescape the string
+		//IMPORTANT MAKE THIS WORK
+		//e.setNewValue(resolverRegistry.unescapeString(attributeValue));
+	}
 }
 
 



Mime
View raw message