MatrixBerryCore
HandleObjectCloner.m
Go to the documentation of this file.
1 classdef HandleObjectCloner<handle
2  properties (Access=private)
3  comparisonMode=mxberry.core.obj.ObjectComparisonMode.UserDefined;
4  end
5  %
6  methods (Access=protected)
7  function blobComparisonHook(self)
8  end
9  end
10  methods (Access=protected, Sealed)
11  function prevMode=setComparisonMode(self,comparisonMode)
12  if nargout>0
13  prevMode=self.getComparisonMode();
14  end
15  [self.comparisonMode]=deal(comparisonMode);
16  end
17  %
18  function comparisonMode=getComparisonMode(self)
19  import mxberry.core.throwerror;
20  if numel(self)==1
21  comparisonMode=self.comparisonMode;
22  elseif isempty(self)
23  comparisonMode=...
24  mxberry.core.obj.ObjectComparisonMode.UserDefined;
25  else
26  isAllCompModeEq=isequal(self.comparisonMode);
27  if ~isAllCompModeEq
28  throwerror('wrongInput',['all elements of an object',...
29  'array are expected to have the same value',...
30  'of comparisonMode']);
31  end
32  comparisonMode=self(1).comparisonMode;
33  end
34  end
35  end
36  %
37  methods (Access=protected)
38  function propCheckCMat=getHandleClonerIsEqualPropCheckCMat(self,propNameList)
39  import mxberry.core.obj.ObjectComparisonMode;
40  switch self.getComparisonMode
41  case ObjectComparisonMode.UserDefined
42  isAsHandleDefault=false;
43  isAsBlobDefault=false;
44  case ObjectComparisonMode.Blob
45  isAsBlobDefault=true;
46  isAsHandleDefault=false;
47  case ObjectComparisonMode.Handle
48  isAsBlobDefault=false;
49  isAsHandleDefault=true;
50  end
51  %
52  propCheckCMat={'asHandle','asBlob','propEqScalarList','compareClass';...
53  isAsHandleDefault,isAsBlobDefault,cell(1,0),true;...
54  'isscalar(x)&&islogical(x)','isscalar(x)&&islogical(x)',...
55  'iscell(x)&&(isrow(x)||(max(size(x))<=1))',...
56  'isscalar(x)&&islogical(x)'};
57  if nargin>1
58  [isThereVec,indThereVec]=ismember(lower(propNameList),...
59  lower(propCheckCMat(1,:)));
60  if ~all(isThereVec)
61  throwerror('wrongInput','not all properties are know');
62  end
63  propCheckCMat=propCheckCMat(:,indThereVec);
64  end
65  end
66  end
67  methods (Access=private,Static)
68  function isPositive=isMe(inpObj)
69  curClassName=mfilename('class');
70  isPositive=isa(inpObj,curClassName);
71  end
72  end
73  methods (Access=protected,Sealed)
74  function [regArgList,propEqScalarList]=parseEqScalarProps(self,eqPropCheckCMat,propListToParse)
75  import mxberry.core.parseparams;
76  import mxberry.core.parseparext;
77  %
78  propCheckCMat=self.getHandleClonerIsEqualPropCheckCMat(...
79  'propEqScalarList');
80  nProps=size(eqPropCheckCMat,2);
81  [regArgList,~,eqRelatedPropValList]=...
82  mxberry.core.parseparext(propListToParse,...
83  eqPropCheckCMat,...
84  'propRetMode','list','isDefaultPropSpecVec',...
85  false(1,nProps));
86  %
87  [regArgList,~,propEqScalarList]=...
88  parseparext(regArgList,propCheckCMat);
89  %
90  propEqScalarList=[propEqScalarList,eqRelatedPropValList];
91  end
92  end
93  %
94  methods (Sealed)
95  function varargout=ismember(leftObjVec,rightObjVec,varargin)
97  import mxberry.core.ismembersortableobj;
98  prevLeftMode=...
99  leftObjVec.setComparisonMode(ObjectComparisonMode.Blob);
100  prevRightMode=...
101  rightObjVec.setComparisonMode(ObjectComparisonMode.Blob);
102  %
103  try
104  if nargout==0
105  ismembersortableobj(leftObjVec,rightObjVec,varargin{:});
106  else
107  varargout=cell(1,nargout);
108  [varargout{:}]=ismembersortableobj(leftObjVec,...
109  rightObjVec,varargin{:});
110  end
111  leftObjVec.setComparisonMode(prevLeftMode);
112  rightObjVec.setComparisonMode(prevRightMode);
113  catch meObj
114  leftObjVec.setComparisonMode(prevLeftMode);
115  rightObjVec.setComparisonMode(prevRightMode);
116  rethrow(meObj);
117  end
118  end
119  function varargout=unique(inpObjVec,varargin)
121  import mxberry.core.uniquesortableobj;
122  prevMode=inpObjVec.setComparisonMode(ObjectComparisonMode.Blob);
123  try
124  if nargout==0
125  uniquesortableobj(inpObjVec,varargin{:});
126  else
127  varargout=cell(1,nargout);
128  [varargout{:}]=uniquesortableobj(inpObjVec,varargin{:});
129  end
130  inpObjVec.setComparisonMode(prevMode);
131  catch meObj
132  inpObjVec.setComparisonMode(prevMode);
133  rethrow(meObj);
134  end
135  end
136  function [resObjVec,indVec]=sort(inpObjVec)
138  prevMode=inpObjVec.setComparisonMode(ObjectComparisonMode.Blob);
139  try
140  [resObjVec,indVec]=mxberry.core.sort.mergesort(inpObjVec);
141  inpObjVec.setComparisonMode(prevMode);
142  catch meObj
143  inpObjVec.setComparisonMode(prevMode);
144  rethrow(meObj);
145  end
146  end
147  %
148  function isEqArr=ne(varargin)
149  isEqArr=eq(varargin{:});
150  isEqArr=~isEqArr;
151  end
152  %
153  function isEqArr=eq(varargin)
154  import mxberry.core.parseparext;
155  propCheckCMat=...
156  varargin{1}.getHandleClonerIsEqualPropCheckCMat(...
157  'asHandle');
158  [regArgList,~,isAsHandle]=parseparext(varargin,...
159  propCheckCMat,2);
160  if isAsHandle
161  isEqArr=eq@handle(regArgList{:});
162  else
163  isEqArr=isEqualElem(regArgList{:});
164  end
165  end
166  function isLeArr=le(varargin)
167  import mxberry.core.parseparext;
168  propCheckCMat=...
169  varargin{1}.getHandleClonerIsEqualPropCheckCMat(...
170  'asHandle');
171  [regArgList,~,isAsHandle]=parseparext(varargin,...
172  propCheckCMat,2);
173  if isAsHandle
174  isLeArr=le@handle(regArgList{:});
175  else
176  [~,~,signOfDiffArr]=isEqualElem(regArgList{:});
177  isLeArr=signOfDiffArr<=0;
178  end
179  end
180  %
181  function isGeArr=ge(varargin)
182  import mxberry.core.parseparext;
183  propCheckCMat=...
184  varargin{1}.getHandleClonerIsEqualPropCheckCMat(...
185  'asHandle');
186  [regArgList,~,isAsHandle]=parseparext(varargin,...
187  propCheckCMat,2);
188  if isAsHandle
189  isGeArr=ge@handle(regArgList{:});
190  else
191  [~,~,signOfDiffArr]=isEqualElem(regArgList{:});
192  isGeArr=signOfDiffArr>=0;
193  end
194  end
195  %
196  function isLtArr=lt(varargin)
197  import mxberry.core.parseparext;
198  propCheckCMat=...
199  varargin{1}.getHandleClonerIsEqualPropCheckCMat(...
200  'asHandle');
201  [regArgList,~,isAsHandle]=parseparext(varargin,...
202  propCheckCMat,2);
203  if isAsHandle
204  isLtArr=lt@handle(regArgList{:});
205  else
206  [~,~,signOfDiffArr]=isEqualElem(regArgList{:});
207  isLtArr=signOfDiffArr<0;
208  end
209  end
210  %
211  function isGtArr=gt(varargin)
212  import mxberry.core.parseparext;
213  propCheckCMat=...
214  varargin{1}.getHandleClonerIsEqualPropCheckCMat(...
215  'asHandle');
216  [regArgList,~,isAsHandle]=parseparext(varargin,...
217  propCheckCMat,2);
218  if isAsHandle
219  isGtArr=gt@handle(regArgList{:});
220  else
221  [~,~,signOfDiffArr]=isEqualElem(regArgList{:});
222  isGtArr=signOfDiffArr>0;
223  end
224  end
225  %
226  function isEq=isequal(varargin)
227  isEq=isEqual(varargin{:});
228  end
229  function isEq=isequaln(varargin)
230  isEq=isequal(varargin{:});
231  end
232  function isEq=isequalwithequalnans(varargin)
233  isEq=isequal(varargin{:});
234  end
235  end
236  %
237  methods
238  function [isEq,reportStr]=isEqual(varargin)
239  import mxberry.core.throwerror;
240  import mxberry.core.parseparext;
241  import mxberry.core.parseparams;
242  NOT_EQ_STR='(object arrays #%d and #%d):%s';
243  %
244  indObj=find(cellfun(@(x)isa(x,mfilename('class')),varargin),...
245  1,'first');
246  propCheckCMat=...
247  varargin{indObj}.getHandleClonerIsEqualPropCheckCMat();
248  %
249  isReportRequired=nargout>1;
250  %
251  [objList,~,isFullCheck,isAsHandle,isAsBlob,propEqScalarList,...
252  isClassCompared]=...
253  parseparext(varargin,...
254  [{'isfullcheck';false;'isscalar(x)&&islogical(x)'},...
255  propCheckCMat]);
256  %
257  checkIsHandleIsBlob(isAsHandle,isAsBlob);
258  if isAsBlob&&~isClassCompared
259  throwerror('wrongInput',['isClassCompared cannot be ',...
260  'set to false when isAsBlob is true']);
261  end
262  %
263  nObj=length(objList);
264  if nObj==1
265  throwerror('wrongInput','Not enough input arguments');
266  end
267  %
268  if isReportRequired
269  reportStr='';
270  reportStrList=cell(1,nObj-1);
271  curReportCell=cell(1,1);
272  else
273  curReportCell={};
274  end
275  isEq=true;
276  for iObj=1:nObj-1
277  obj1=objList{iObj};
278  obj2=objList{iObj+1};
279  if isequal(size(obj1),size(obj2))
280  if ~isClassCompared||isequal(class(obj1),class(obj2))
281  if isAsHandle
282  isEqCur=obj1.eq(obj2,'asHandle',true);
283  if isReportRequired
284  if ~isEqCur
285  reportStrCur='handles are different';
286  else
287  reportStrCur='';
288  end
289  end
290  else
291  if isAsBlob
292  [isEqCur,curReportCell{:}]=...
293  obj1.isEqualScalarAsBlobInternal(...
294  obj2,propEqScalarList{:});
295  else
296  [isEqCurMat,curReportCell{:}]=...
297  obj1.isEqualElem(...
298  obj2,'propEqScalarList',...
299  propEqScalarList,'compareClass',...
300  isClassCompared);
301  %
302  isEqCur=all(isEqCurMat(:));
303  end
304  if isReportRequired
305  reportStrCur=curReportCell{1};
306  end
307  end
308  %
309  isEqCur=all(isEqCur(:));
310  if isReportRequired&&~isempty(reportStrCur)
311  reportStrList{iObj}=sprintf(...
312  NOT_EQ_STR,...
313  iObj,iObj+1,reportStrCur);
314  end
315  else
316  isEqCur=false;
317  if nargout>1
318  reportStrList{iObj}=sprintf(...
319  NOT_EQ_STR,...
320  iObj,iObj+1,'classes are not equal');
321  end
322  end
323  else
324  isEqCur=false;
325  if nargout>1
326  reportStrList{iObj}=sprintf(...
327  NOT_EQ_STR,...
328  iObj,iObj+1,'sizes are not equal');
329  end
330  end
331  isEq=isEq&&isEqCur;
332  if ~(isEq||isFullCheck)
333  break;
334  end
335  end
336  if isReportRequired
337  reportStrList(cellfun('isempty',reportStrList))=[];
338  if length(reportStrList)>1
339  reportStr=mxberry.core.string.catwithsep(reportStrList,...
340  sprintf('\n'));
341  elseif ~isempty(reportStrList)
342  reportStr=reportStrList{:};
343  end
344  end
345  end
346  %
347  function [isEqArr,reportStr,signOfDiffArr]=isEqualElem(selfArr,otherArr,varargin)
348  import mxberry.core.throwerror;
349  import mxberry.core.parseparext;
350  %
351  propCheckCMat=...
352  selfArr.getHandleClonerIsEqualPropCheckCMat();
353  %
354  [~,~,isAsHandle,isAsBlob,propEqScalarList,isClassCompared]=...
355  parseparext(varargin,propCheckCMat,0);
356  %
357  checkIsHandleIsBlob(isAsHandle,isAsBlob);
358  %
359  isReportRequired=nargout>1;
360  isSignOfDiffRequired=nargout>2;
361  %
362  sizeVec=size(selfArr);
363  if ~isequal(sizeVec,size(otherArr))
364  if numel(selfArr)==1
365  sizeVec=size(otherArr);
366  selfArr=repmat(selfArr,sizeVec);
367  elseif numel(otherArr)==1
368  otherArr=repmat(otherArr,sizeVec);
369  else
370  error('MATLAB:dimagree',...
371  'Matrix dimensions must agree.');
372  end
373  end
374  nElems=numel(selfArr);
375  if isReportRequired
376  reportStr='';
377  reportStrList=cell(1,nElems);
378  curExtraOutArgList=cell(1,nargout-1);
379  else
380  curExtraOutArgList={};
381  end
382 
383  isEqHandleMat=selfArr.eq(otherArr,'asHandle',true);
384  %
385  if isSignOfDiffRequired
386  signOfDiffArr=nan(size(isEqHandleMat));
387  signOfDiffArr(isEqHandleMat)=0;
388  end
389  %
390  if ~all(isEqHandleMat(:))
391  isEqArr=true(sizeVec);
392  if isClassCompared&&(~isa(otherArr,class(selfArr)))
393  isEqArr(:)=false;
394  if nargout>1
395  reportStr='Not equal classes of objects';
396  end
397  else
398  if ~isempty(isEqArr)
399  for iElem=1:nElems
400 
401  isEqCur=isEqHandleMat(iElem);
402  %
403  if ~isEqCur
404  if isAsHandle
405  if isReportRequired
406  reportStrCur='handles are different'; end
407  else
408  if isAsBlob
409  [isEqCur,curExtraOutArgList{:}]=...
410  selfArr(iElem).isEqualScalarAsBlobInternal(...
411  otherArr(iElem),propEqScalarList{:});
412  %
413  if isSignOfDiffRequired
414  signOfDiffArr(iElem)=curExtraOutArgList{2};
415  end
416  else
417  [isEqCur,curExtraOutArgList{:}]=...
418  selfArr(iElem).isEqualScalarInternal(...
419  otherArr(iElem),propEqScalarList{:});
420  end
421  if isReportRequired
422  reportStrCur=curExtraOutArgList{1};
423  end
424  end
425  end
426  %
427  if (nargout>1)&&~isempty(reportStrCur)
428  reportStrList{iElem}=sprintf(...
429  '(element #%d):%s',iElem,...
430  reportStrCur);
431  end
432  isEqArr(iElem)=isEqCur;
433  end
434  if nargout>1
435  reportStrList(...
436  cellfun('isempty',reportStrList))=[];
437  if length(reportStrList)>1
438  reportStr=mxberry.core.string.catwithsep(...
439  reportStrList,sprintf('\n'));
440  elseif ~isempty(reportStrList)
441  reportStr=reportStrList{:};
442  end
443  end
444  end
445  end
446  else
447  isEqArr=isEqHandleMat;
448  end
449  if isSignOfDiffRequired
450  if any(isnan(signOfDiffArr(:)))
451  throwerror('wrongInput:signNotDefForAllElems',...
452  ['sign of difference is ',...
453  'not assigned for all elements']);
454  end
455  end
456  end
457  end
458  methods (Access=private)
459  function [isEq,reportStr,signOfDiff]=isEqualScalarAsBlobInternal(leftObj,rightObj)
460  leftObj.blobComparisonHook();
461  if nargout<=2
462  isEq=false;
463  isEqSize=isequal(size(leftObj),size(rightObj));
464  if isEqSize
465  isEqClass=isequal(class(leftObj),class(rightObj));
466  if ~isEqClass
467  reportStr='classes are different';
468  else
469  isEq=true;
470  end
471  else
472  if nargout>1
473  reportStr='sizes are different';
474  end
475  end
476  else
477  isEq=true;
478  end
479  %
480  if isEq
481  leftBlobVec=getByteStreamFromArray(leftObj);
482  rightBlobVec=getByteStreamFromArray(rightObj);
483  nLeftElems=numel(leftBlobVec);
484  nRightElems=numel(rightBlobVec);
485  %
486  isEqBlobSize=nLeftElems==nRightElems;
487  isEq=isEqBlobSize&&isequal(leftBlobVec,rightBlobVec);
488  if nargout>1
489  if isEq
490  reportStr='';
491  else
492  if isEqBlobSize
493  reportStr='blobs are different';
494  else
495  reportStr='blob sizes are different';
496  end
497  end
498  if nargout>2
499  if ~isEq
500  if ~isEqBlobSize
501  if nLeftElems<nRightElems
502  leftBlobVec=[leftBlobVec,...
503  zeros(1,nRightElems-nLeftElems)];
504  else
505  leftBlobVec=[rightBlobVec,...
506  zeros(1,nLeftElems)-nRightElems];
507  end
508  end
509  [~,indSortedVec]=sortrows([leftBlobVec;rightBlobVec]);
510  signOfDiff=indSortedVec(2)-indSortedVec(1);
511  else
512  signOfDiff=0;
513  end
514  end
515  end
516  end
517  end
518  end
519  %
520  methods (Access=protected)
521  function [isOk,reportStr,signOfDiff]=isEqualScalarInternal(self,otherObj,varargin)
522  isOk=self.eq(otherObj,'asHandle',true);
523  if nargout>1
524  if isOk
525  reportStr='';
526  else
527  reportStr='handles are different';
528  end
529  if nargout>2
530  signOfDiff=nan;
531  end
532  end
533  end
534  end
535  methods
536  function obj=clone(self,varargin)
537  if isempty(varargin)
538  %Performance optimization
539  obj = getArrayFromByteStream(getByteStreamFromArray(self));
540  else
541  obj=self.createInstance(self,varargin{:});
542  end
543  end
544  function resObj=createInstance(self,varargin)
545  p=metaclass(self);
546  resObj=feval(p.Name,varargin{:});
547  end
548  end
549 end
550 function checkIsHandleIsBlob(isAsHandle,isAsBlob)
551 import mxberry.core.throwerror;
552 if isAsBlob&&isAsHandle
553  throwerror('wrongInput:blobAndHandleIncompatible',['isAsBlob and isAsHandle ',...
554  'cannot both be true']);
555 end
556 end
function uniquesortableobj(in inpVec)
UNIQUE implementation strictly for sortable entities i.e. for those that have 1) full order defined b...
function throwerror(in msgTag, in varargin)
THROWERROR works similarly to built-in ERROR function in case when there is no output arguments but s...
function ismembersortableobj(in firstVec, in secVec)
ISMEMBER implementation strictly for sortable entities i.e. for those that have 1) full order defined...
function parseparext(in args, in propNameValMat, in varargin)
PARSEPAREXT behaves in the same way as mxberry.core.parseparams but returns property values in a more...
function repmat(in inpArray, in varargin)
function unique(in inpVec)
UNIQUE for arrays of any type.
function ismember(in leftVec, in rightVec, in varargin)
ISMEMBER - ismember implementation for arrays of any type.