1 /*
  2  *  Copyright (C) 2008-2009 WaveMaker Software, Inc.
  3  *
  4  *  This file is part of the WaveMaker Client Runtime.
  5  *
  6  *  Licensed under the Apache License, Version 2.0 (the "License");
  7  *  you may not use this file except in compliance with the License.
  8  *  You may obtain a copy of the License at
  9  *
 10  *      http://www.apache.org/licenses/LICENSE-2.0
 11  *
 12  *  Unless required by applicable law or agreed to in writing, software
 13  *  distributed under the License is distributed on an "AS IS" BASIS,
 14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15  *  See the License for the specific language governing permissions and
 16  *  limitations under the License.
 17  */
 18 dojo.provide('wm.base.lib.data');
 19 
 20 wm.data = wm.data || {};
 21 
 22 dojo.mixin(wm.data, {
 23 	// returns fields that should be included for type
 24 	getIncludeFields: function(inTypeName) {
 25 		var
 26 			pi, fields=[],
 27 			schema = wm.typeManager.getTypeSchema(inTypeName);
 28 		for (var i in schema) {
 29 			pi = schema[i];
 30 			if (pi.include && pi.include.length) {
 31 				// composite key
 32 				if (wm.typeManager.isStructuredType(pi.type)) {
 33 					var compSchema = wm.typeManager.getTypeSchema(pi.type);
 34 					for (var j in compSchema)
 35 						fields.push(i + "." + j);
 36 				} else
 37 					fields.push(i);
 38 			}
 39 		}
 40 		return fields;
 41 	},
 42 	// Reports if given data of type has include data.
 43 	// This is equivalent to having primary key information
 44 	// that is necessary for initiating update and delete operations.
 45 	// By default related structured types are not checked for include data
 46 	// That information is typically not required for update / delete operations.
 47 	hasIncludeData: function(inTypeName, inData) {
 48 		if (!inData || wm.isEmpty(inData))
 49 			return false;
 50 		var fields = this.getIncludeFields(inTypeName);
 51 		for (var i=0, f; f=fields[i]; i++)
 52 			if (dojo.getObject(f, false, inData) === undefined)
 53 				return;
 54 		return true;
 55 	},
 56 	// Reports if given data of type contains necessary contents
 57 	// to perform given operation. 
 58 	hasOperationData: function(inOperation, inTypeName, inData) {
 59 		if (!wm.typeManager.getLiveService(inTypeName))
 60 			return false;
 61 		switch(inOperation) {
 62 			// read ok if we provide no data or we have necessary root include data
 63 			case "read":
 64 				return !inData || wm.data.hasIncludeData(inTypeName, inData);
 65 			// root include data is required for delete and update
 66 			case "delete":
 67 			case "update":
 68 				return wm.data.hasIncludeData(inTypeName, inData);
 69 			// for insert all required root and provided related required data is necessary
 70 			case "insert":
 71 				return wm.data.hasRequiredData(inOperation, inTypeName, inData, true);
 72 		}
 73 	},
 74 	// Reports if given data of type contains all required data
 75 	// This info is helpful for determining if there's enough data to perform an insert operation
 76 	// In this case we want to check structured related data also.
 77 	// Operation and the structured data flag are provided for additional flexibility...
 78 	hasRequiredData: function(inOperation, inTypeName, inData, inCheckStructured) {
 79 		var schema = wm.typeManager.getTypeSchema(inTypeName),
 80 			s, d, isStructured, hasData, missingRequired, hasExcluded;
 81 		for (var i in schema) {
 82 			s = schema[i];
 83 			isStructured = wm.typeManager.isStructuredType(s.type);
 84 			d = inData && inData[i];
 85 			// check structured type
 86 			if (isStructured && inCheckStructured) {
 87 				if ((d || s.required) && !s.isList && !this.hasRequiredData(s.type, d, inCheckStructured))
 88 					return false;
 89 			} else {
 90 				hasData = (d !== undefined);
 91 				missingRequired = s.required && !hasData;
 92 				// return false if we have excluded data or missing required data.
 93 				if (dojo.indexOf(s.exclude, inOperation) != -1 ? hasData : missingRequired)
 94 					return false;
 95 			}
 96 		}
 97 		return true;
 98 	},
 99 	// binding
100 	clearBinding: function(inObject, inTargetProperty) {
101 		var w = wm.data.getPropWire(inObject, inTargetProperty);
102 		if (w) {
103 			var b = w.owner, target = w.target, tp = w.targetProperty;
104 			// note: removing wire may have side-effects so reset value with care after removing.
105 			if (b)
106 				b.removeWire(w.getWireId());
107 			// reset value here.
108 			if (target && tp)
109 				target.setValue(tp, "");
110 		}
111 	},
112 	getPropWire: function(inTargetObject, inTargetProperty) {
113 		var
114 			tp = inTargetProperty,
115 			tobj = inTargetObject,
116 			binding = tobj && tobj.$.binding,
117 			// Note: target bindings are stored in wires hash by targetProperty
118 			// source bindings has targetId appended so they will be ignored below
119 			w = binding && binding.wires[tp];
120 		// if there's a target binding, return it
121 		if (w)
122 			return w;
123 		// FIXME: design check...
124 		var ownerApp = tobj && tobj.isDesignLoaded() ? studio.application : app;
125 		// otherwise, if the object is owned by the application try to return a source binding.
126 		if (tobj && tobj.isOwnedBy(ownerApp))
127 			return wm.data.findSourceWire((tobj||0).getId(), tp);
128 	},
129 	findSourceWire: function(inTargetId, inProp) {
130 		if (inTargetId) {
131 			var c, o, id, wires, w;
132 			// search all components, wee...
133 			for (var i in wm.Component.byId) {
134 				c = wm.Component.byId[i];
135 				// FIXME: design check...
136 				if ((c instanceof wm.Binding) && (c.isDesignLoaded() || !(window.studio && window.studio._isWaveMakerStudio))) {
137 					var wires = c.findWiresByProps({targetId: inTargetId, targetProperty: inProp});
138 					if (wires.length)
139 						return wires[0];
140 				}
141 			}
142 		}
143 	},
144 	// FIXME: deprecated
145 	/*getPropertyBindWire: function(inBinding, inTargetProperty) {
146 		var wires = inBinding.wires, w;
147 		for (var i in wires) {
148 			w = wires[i];
149 			if (w.targetProperty == inTargetProperty)
150 				return w;
151 		}
152 	},*/
153 	getPropBindSource: function(inTargetObject, inTargetProperty) {
154 		var w = wm.data.getPropWire(inTargetObject, inTargetProperty);
155 		if (w)
156 			return inTargetObject.getValueById(w.source);
157 	},
158 	// a simple comparator
159 	compare: function(a, b) {
160 		return a === b ? 0 :
161 			a === undefined ? -1 :
162 			b === undefined ? 1 :
163 			b === null ? 1 :
164 			a > b ? 1 :
165 			-1;
166 	}
167 });