diff --git a/ProductApp/Pods/Pods.xcodeproj/xcuserdata/gongzuo.xcuserdatad/xcschemes/xcschememanagement.plist b/ProductApp/Pods/Pods.xcodeproj/xcuserdata/gongzuo.xcuserdatad/xcschemes/xcschememanagement.plist
index 508d6ee..c4a628f 100644
--- a/ProductApp/Pods/Pods.xcodeproj/xcuserdata/gongzuo.xcuserdatad/xcschemes/xcschememanagement.plist
+++ b/ProductApp/Pods/Pods.xcodeproj/xcuserdata/gongzuo.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -8,166 +8,232 @@
isShown
+ orderHint
+ 1
DZNEmptyDataSet.xcscheme
isShown
+ orderHint
+ 2
FMDB.xcscheme
isShown
+ orderHint
+ 3
GTCommonSDK.xcscheme
isShown
+ orderHint
+ 4
GTExtensionSDK.xcscheme
isShown
+ orderHint
+ 5
GTSDK.xcscheme
isShown
+ orderHint
+ 6
GYSDK.xcscheme
isShown
+ orderHint
+ 7
HXPhotoPicker.xcscheme
isShown
+ orderHint
+ 8
IQKeyboardManager.xcscheme
isShown
+ orderHint
+ 9
LSTTimer.xcscheme
isShown
+ orderHint
+ 11
MBProgressHUD.xcscheme
isShown
+ orderHint
+ 13
MJRefresh.xcscheme
isShown
+ orderHint
+ 14
MOFSPickerManager.xcscheme
isShown
+ orderHint
+ 15
Masonry.xcscheme
isShown
+ orderHint
+ 12
Pods-ProductApp.xcscheme
isShown
+ orderHint
+ 16
PopupKit.xcscheme
isShown
+ orderHint
+ 17
Reachability.xcscheme
isShown
+ orderHint
+ 18
SDAutoLayout.xcscheme
isShown
+ orderHint
+ 19
SDCycleScrollView.xcscheme
isShown
+ orderHint
+ 20
SDWebImage.xcscheme
isShown
+ orderHint
+ 21
SSZipArchive.xcscheme
isShown
+ orderHint
+ 23
SocketRocket.xcscheme
isShown
+ orderHint
+ 22
UITableView+FDTemplateLayoutCell.xcscheme
isShown
+ orderHint
+ 24
UMAPM.xcscheme
isShown
+ orderHint
+ 25
UMCCommonLog.xcscheme
isShown
+ orderHint
+ 26
UMCommon.xcscheme
isShown
+ orderHint
+ 27
UMDevice.xcscheme
isShown
+ orderHint
+ 28
UMLink.xcscheme
isShown
+ orderHint
+ 29
WechatOpenSDK.xcscheme
isShown
+ orderHint
+ 30
YYModel.xcscheme
isShown
+ orderHint
+ 31
YYText.xcscheme
isShown
+ orderHint
+ 32
ZXSDK.xcscheme
isShown
+ orderHint
+ 33
libpag.xcscheme
isShown
+ orderHint
+ 10
SuppressBuildableAutocreation
diff --git a/ProductApp/ProductApp.xcodeproj/project.pbxproj b/ProductApp/ProductApp.xcodeproj/project.pbxproj
index 83b73fe..b3fd68a 100644
--- a/ProductApp/ProductApp.xcodeproj/project.pbxproj
+++ b/ProductApp/ProductApp.xcodeproj/project.pbxproj
@@ -11,6 +11,12 @@
CA0688CD2CD328C500DF7025 /* ToolCheckModel.m in Sources */ = {isa = PBXBuildFile; fileRef = CA0688CC2CD328C500DF7025 /* ToolCheckModel.m */; };
CA0688D02CD3291900DF7025 /* OcrModel.m in Sources */ = {isa = PBXBuildFile; fileRef = CA0688CF2CD3291900DF7025 /* OcrModel.m */; };
CA0688D32CD329AD00DF7025 /* TranslateLanguageModel.m in Sources */ = {isa = PBXBuildFile; fileRef = CA0688D22CD329AD00DF7025 /* TranslateLanguageModel.m */; };
+ CA0688E52CD374CD00DF7025 /* DiffMatchPatch.m in Sources */ = {isa = PBXBuildFile; fileRef = CA0688DC2CD374CB00DF7025 /* DiffMatchPatch.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+ CA0688E62CD374CD00DF7025 /* NSString+UnicharUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = CA0688DE2CD374CC00DF7025 /* NSString+UnicharUtilities.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+ CA0688E72CD374CD00DF7025 /* NSMutableDictionary+DMPExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = CA0688E12CD374CC00DF7025 /* NSMutableDictionary+DMPExtensions.m */; };
+ CA0688E82CD374CD00DF7025 /* NSString+JavaSubstring.m in Sources */ = {isa = PBXBuildFile; fileRef = CA0688E22CD374CC00DF7025 /* NSString+JavaSubstring.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
+ CA0688E92CD374CD00DF7025 /* NSString+UriCompatibility.m in Sources */ = {isa = PBXBuildFile; fileRef = CA0688E32CD374CC00DF7025 /* NSString+UriCompatibility.m */; };
+ CA0689252CD3753F00DF7025 /* DiffMatchPatchCFUtilities.c in Sources */ = {isa = PBXBuildFile; fileRef = CA0689232CD3753F00DF7025 /* DiffMatchPatchCFUtilities.c */; };
CA07CD142CC5E5C500AF41ED /* XieYiAlterView.m in Sources */ = {isa = PBXBuildFile; fileRef = CA07CD132CC5E5C500AF41ED /* XieYiAlterView.m */; };
CA07CD172CC5F25B00AF41ED /* TextbookDetailModel.m in Sources */ = {isa = PBXBuildFile; fileRef = CA07CD162CC5F25B00AF41ED /* TextbookDetailModel.m */; };
CA07CD1A2CC6335400AF41ED /* ExampleCorrectList.m in Sources */ = {isa = PBXBuildFile; fileRef = CA07CD192CC6335400AF41ED /* ExampleCorrectList.m */; };
@@ -531,6 +537,20 @@
CA0688CF2CD3291900DF7025 /* OcrModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OcrModel.m; sourceTree = ""; };
CA0688D12CD329AD00DF7025 /* TranslateLanguageModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TranslateLanguageModel.h; sourceTree = ""; };
CA0688D22CD329AD00DF7025 /* TranslateLanguageModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TranslateLanguageModel.m; sourceTree = ""; };
+ CA0688DB2CD374CB00DF7025 /* NSString+JavaSubstring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+JavaSubstring.h"; sourceTree = ""; };
+ CA0688DC2CD374CB00DF7025 /* DiffMatchPatch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DiffMatchPatch.m; sourceTree = ""; };
+ CA0688DD2CD374CC00DF7025 /* NSMutableDictionary+DMPExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableDictionary+DMPExtensions.h"; sourceTree = ""; };
+ CA0688DE2CD374CC00DF7025 /* NSString+UnicharUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+UnicharUtilities.m"; sourceTree = ""; };
+ CA0688DF2CD374CC00DF7025 /* NSString+UriCompatibility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+UriCompatibility.h"; sourceTree = ""; };
+ CA0688E02CD374CC00DF7025 /* NSString+UnicharUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+UnicharUtilities.h"; sourceTree = ""; };
+ CA0688E12CD374CC00DF7025 /* NSMutableDictionary+DMPExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableDictionary+DMPExtensions.m"; sourceTree = ""; };
+ CA0688E22CD374CC00DF7025 /* NSString+JavaSubstring.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+JavaSubstring.m"; sourceTree = ""; };
+ CA0688E32CD374CC00DF7025 /* NSString+UriCompatibility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+UriCompatibility.m"; sourceTree = ""; };
+ CA0688E42CD374CC00DF7025 /* DiffMatchPatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DiffMatchPatch.h; sourceTree = ""; };
+ CA0689212CD3753F00DF7025 /* DiffMatchPatchCFUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DiffMatchPatchCFUtilities.h; sourceTree = ""; };
+ CA0689222CD3753F00DF7025 /* MinMaxMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MinMaxMacros.h; sourceTree = ""; };
+ CA0689232CD3753F00DF7025 /* DiffMatchPatchCFUtilities.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DiffMatchPatchCFUtilities.c; sourceTree = ""; };
+ CA0689242CD3753F00DF7025 /* DiffMatchPatch_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DiffMatchPatch_Prefix.pch; sourceTree = ""; };
CA07CD122CC5E5C500AF41ED /* XieYiAlterView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XieYiAlterView.h; sourceTree = ""; };
CA07CD132CC5E5C500AF41ED /* XieYiAlterView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XieYiAlterView.m; sourceTree = ""; };
CA07CD152CC5F25B00AF41ED /* TextbookDetailModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TextbookDetailModel.h; sourceTree = ""; };
@@ -1571,6 +1591,35 @@
path = Pods;
sourceTree = "";
};
+ CA0688DA2CD374BC00DF7025 /* Diff */ = {
+ isa = PBXGroup;
+ children = (
+ CA0689202CD3753400DF7025 /* other */,
+ CA0688E42CD374CC00DF7025 /* DiffMatchPatch.h */,
+ CA0688DC2CD374CB00DF7025 /* DiffMatchPatch.m */,
+ CA0688DD2CD374CC00DF7025 /* NSMutableDictionary+DMPExtensions.h */,
+ CA0688E12CD374CC00DF7025 /* NSMutableDictionary+DMPExtensions.m */,
+ CA0688DB2CD374CB00DF7025 /* NSString+JavaSubstring.h */,
+ CA0688E22CD374CC00DF7025 /* NSString+JavaSubstring.m */,
+ CA0688E02CD374CC00DF7025 /* NSString+UnicharUtilities.h */,
+ CA0688DE2CD374CC00DF7025 /* NSString+UnicharUtilities.m */,
+ CA0688DF2CD374CC00DF7025 /* NSString+UriCompatibility.h */,
+ CA0688E32CD374CC00DF7025 /* NSString+UriCompatibility.m */,
+ );
+ path = Diff;
+ sourceTree = "";
+ };
+ CA0689202CD3753400DF7025 /* other */ = {
+ isa = PBXGroup;
+ children = (
+ CA0689242CD3753F00DF7025 /* DiffMatchPatch_Prefix.pch */,
+ CA0689232CD3753F00DF7025 /* DiffMatchPatchCFUtilities.c */,
+ CA0689212CD3753F00DF7025 /* DiffMatchPatchCFUtilities.h */,
+ CA0689222CD3753F00DF7025 /* MinMaxMacros.h */,
+ );
+ path = other;
+ sourceTree = "";
+ };
CA0C3A802CB4C25B00E01A72 /* 换个思路 */ = {
isa = PBXGroup;
children = (
@@ -3287,6 +3336,7 @@
CABD8C8A2CBF919D009A5E5E /* view */ = {
isa = PBXGroup;
children = (
+ CA0688DA2CD374BC00DF7025 /* Diff */,
CABD8C8B2CBF91B0009A5E5E /* GongJuWenBenView.h */,
CABD8C8C2CBF91B0009A5E5E /* GongJuWenBenView.m */,
);
@@ -4445,6 +4495,7 @@
knownRegions = (
en,
Base,
+ English,
);
mainGroup = CB489DBC27449D5C00DA044A;
productRefGroup = CB489DC627449D5C00DA044A /* Products */;
@@ -4739,6 +4790,7 @@
CA0D08712CA54D4B0086855E /* ShouYeLiShiJiLuXZTableViewCell.m in Sources */,
CB489F742744A0BD00DA044A /* MCTabBar.m in Sources */,
CB489FB12744A0BD00DA044A /* FSActionSheetCell.m in Sources */,
+ CA0688E92CD374CD00DF7025 /* NSString+UriCompatibility.m in Sources */,
CA11ED782CA69B5200209DFC /* GaiXieRunSeView.m in Sources */,
CB489F6D2744A0BD00DA044A /* UIView+Additions.m in Sources */,
CAB0D3752CAA47F3009BF67D /* ZhiNengXieZuoViewController.m in Sources */,
@@ -4766,6 +4818,7 @@
CA0D085A2CA543E40086855E /* ShouYeZCPPTTableViewCell.m in Sources */,
CA6D547A2CCB9F00001B530A /* CorrectWriteCorrectModel.m in Sources */,
CA81819D2C9E9C6600EE7E6E /* StartKTXZPGDetailViewController.m in Sources */,
+ CA0688E52CD374CD00DF7025 /* DiffMatchPatch.m in Sources */,
CB489FA32744A0BD00DA044A /* ZJContentView.m in Sources */,
CA5D02E62CC0DD6B007B3BA5 /* UserUploadModel.m in Sources */,
CA5D02EA2CC0DD6B007B3BA5 /* UserShareModel.m in Sources */,
@@ -4773,6 +4826,7 @@
CA0688D32CD329AD00DF7025 /* TranslateLanguageModel.m in Sources */,
CAC8064E2CA119F800C21AA7 /* StartZWPIDetailView.m in Sources */,
CA5D02E32CC0DD6B007B3BA5 /* UserModel.m in Sources */,
+ CA0688E72CD374CD00DF7025 /* NSMutableDictionary+DMPExtensions.m in Sources */,
CB489F6C2744A0BD00DA044A /* RadianDisView.m in Sources */,
CA07CD1A2CC6335400AF41ED /* ExampleCorrectList.m in Sources */,
CA487D8A2CA3AB6B00AE773B /* UITableView+MoveCell.m in Sources */,
@@ -4886,6 +4940,7 @@
CB489F6E2744A0BD00DA044A /* UIButton+EnlargeTouchArea.m in Sources */,
CB489F972744A0BD00DA044A /* WkWebviewViewController.m in Sources */,
CB489F692744A0BD00DA044A /* CalendarDataDays.m in Sources */,
+ CA0689252CD3753F00DF7025 /* DiffMatchPatchCFUtilities.c in Sources */,
CB489F932744A0BD00DA044A /* PutImageModel.m in Sources */,
CA6B97252CBCEBE3000213F3 /* AIChatTextTableViewCell.m in Sources */,
CA11ED5D2CA653F600209DFC /* CamreImagesView.m in Sources */,
@@ -4971,6 +5026,7 @@
CA11ED612CA6556900209DFC /* CamreImagesCollectionViewCell.m in Sources */,
CAF76E942CBE69F900825E5E /* GongJuCreateTimeView.m in Sources */,
CA0D08452CA5316B0086855E /* ShouYeShenFenView.m in Sources */,
+ CA0688E82CD374CD00DF7025 /* NSString+JavaSubstring.m in Sources */,
CB489FB52744A0BD00DA044A /* HYTimePickerView.m in Sources */,
CA4257A32CA29BF900A36A10 /* StartZNPPTDaGangAlterView.m in Sources */,
CA0D08832CA5682D0086855E /* CamreViewController.m in Sources */,
@@ -4980,6 +5036,7 @@
CA22D1CA2CD07D9100CA7E93 /* CorrectSaveModel.m in Sources */,
CAC8066C2CA16DCC00C21AA7 /* StartPZXZViewController.m in Sources */,
CB489F722744A0BD00DA044A /* MCTabBarController.m in Sources */,
+ CA0688E62CD374CD00DF7025 /* NSString+UnicharUtilities.m in Sources */,
CABD8CAB2CBFB39F009A5E5E /* GongJuMinGanCiController.m in Sources */,
CA5D03642CC0DE8D007B3BA5 /* YaoQingViewController.m in Sources */,
CB489F9F2744A0BD00DA044A /* LogView.m in Sources */,
diff --git a/ProductApp/ProductApp.xcworkspace/xcuserdata/gongzuo.xcuserdatad/UserInterfaceState.xcuserstate b/ProductApp/ProductApp.xcworkspace/xcuserdata/gongzuo.xcuserdatad/UserInterfaceState.xcuserstate
index 7b49275..e8805cc 100644
Binary files a/ProductApp/ProductApp.xcworkspace/xcuserdata/gongzuo.xcuserdatad/UserInterfaceState.xcuserstate and b/ProductApp/ProductApp.xcworkspace/xcuserdata/gongzuo.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/ProductApp/ProductApp.xcworkspace/xcuserdata/gongzuo.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/ProductApp/ProductApp.xcworkspace/xcuserdata/gongzuo.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
index acc3975..4077daa 100644
--- a/ProductApp/ProductApp.xcworkspace/xcuserdata/gongzuo.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
+++ b/ProductApp/ProductApp.xcworkspace/xcuserdata/gongzuo.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -148,38 +148,6 @@
landmarkType = "7">
-
-
-
-
-
-
-
-
+
+
+
+
diff --git a/ProductApp/ProductApp/Info.plist b/ProductApp/ProductApp/Info.plist
index 7cc10fc..ef54a2f 100644
--- a/ProductApp/ProductApp/Info.plist
+++ b/ProductApp/ProductApp/Info.plist
@@ -2,6 +2,19 @@
+ CFBundleURLTypes
+
+
+ CFBundleTypeRole
+ Editor
+ CFBundleURLName
+ wx
+ CFBundleURLSchemes
+
+ wx31efd88bd33ae068
+
+
+
GT_MinimumOSVersion
11
LSApplicationQueriesSchemes
diff --git a/ProductApp/ProductApp/PrefixHeader.pch b/ProductApp/ProductApp/PrefixHeader.pch
index 818a8d6..d06a4aa 100644
--- a/ProductApp/ProductApp/PrefixHeader.pch
+++ b/ProductApp/ProductApp/PrefixHeader.pch
@@ -8,7 +8,8 @@
#ifndef PrefixHeader_pch
#define PrefixHeader_pch
-
+#ifdef __OBJC__
+
#import
#import
#import
@@ -42,5 +43,9 @@
#import "ViewLable.h"
#import "LoadAlterView.h"
+
+#endif
+
+
#endif /* PrefixHeader_pch */
diff --git a/ProductApp/ProductApp/ProductMain/NetWorkManager/models/ToolCheckModel.h b/ProductApp/ProductApp/ProductMain/NetWorkManager/models/ToolCheckModel.h
index 3bc394f..c61d6d2 100644
--- a/ProductApp/ProductApp/ProductMain/NetWorkManager/models/ToolCheckModel.h
+++ b/ProductApp/ProductApp/ProductMain/NetWorkManager/models/ToolCheckModel.h
@@ -11,7 +11,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface ToolCheckModel : BaseModel
///
-@property (nonatomic , strong) NSString *data;
+@property (nonatomic , strong) NSArray *data;
@end
NS_ASSUME_NONNULL_END
diff --git a/ProductApp/ProductApp/ProductMain/工具/GongJuViewController.m b/ProductApp/ProductApp/ProductMain/工具/GongJuViewController.m
index 210461b..127fafd 100644
--- a/ProductApp/ProductApp/ProductMain/工具/GongJuViewController.m
+++ b/ProductApp/ProductApp/ProductMain/工具/GongJuViewController.m
@@ -12,8 +12,15 @@
#import "NetWorkManager.h"
#import "GongJuSearchViewController.h"
+#import "StartPayViewController.h"
+#import "KeTangXieZuoViewController.h"
+
+#import "ShouYeLingYuViewController.h"
+#import "ShouYeShenFenViewController.h"
@interface GongJuViewController ()
+///
+@property (nonatomic , strong) UIButton *btnianji;
//轮播
@property (nonatomic, strong) SDCycleScrollView *cleScrollView;
@@ -31,6 +38,14 @@
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
+ [self updateBtShow];
+
+ if([UserInfoModel shareModel].isupdataJiaoyu && self.arrdata.count>0)
+ {
+ [UserInfoModel shareModel].isupdataJiaoyu = NO;
+ [self getdata];
+ }
+
}
- (void)viewDidLoad {
[super viewDidLoad];
@@ -43,12 +58,12 @@
SDCycleScrollView *cleScrollView = [SDCycleScrollView cycleScrollViewWithFrame:CGRectZero imageNamesGroup:@[]];
cleScrollView.scrollDirection = UICollectionViewScrollDirectionHorizontal;
cleScrollView.delegate = self;
- cleScrollView.backgroundColor = [UIColor whiteColor];
+// cleScrollView.backgroundColor = [UIColor whiteColor];
cleScrollView.titleLabelBackgroundColor = [UIColor clearColor];
cleScrollView.titleLabelTextColor = [UIColor darkGrayColor];
cleScrollView.titleLabelTextFont = [UIFont systemFontOfSize:[Tools sizeFont:14]];
cleScrollView.autoScrollTimeInterval = 3.0;
- cleScrollView.placeholderImage = [UIImage imageNamed:@"home_bannernomo"];
+// cleScrollView.placeholderImage = [UIImage imageNamed:@"home_bannernomo"];
cleScrollView.bannerImageViewContentMode = UIViewContentModeScaleAspectFill;
[cleScrollView setShowPageControl:YES];
cleScrollView.pageControlBottomOffset = -5;
@@ -61,10 +76,16 @@
make.height.offset(90);
}];
[cleScrollView.layer setMasksToBounds:YES];
- [cleScrollView.layer setCornerRadius:8];
+ [cleScrollView.layer setCornerRadius:17];
_cleScrollView = cleScrollView;
- cleScrollView.imageURLStringsGroup = @[@"",@"",@""];
+ NSArray *arrbanner = [[UserInfoModel shareModel].config objectForKey:@"client.tools.banner.urls"];
+ NSMutableArray *arrurls = [NSMutableArray new];
+ for(NSDictionary *dic in arrbanner)
+ {
+ [arrurls addObject:[Tools isStringnil:[dic objectForKey:@"image"]]];
+ }
+ cleScrollView.imageURLStringsGroup = arrurls;
[self drawUI];
@@ -121,6 +142,7 @@
[btnianji setIconInRightWithSpacing:6];
[btnianji setTag:0];
[btnianji addTarget:self action:@selector(topAction:) forControlEvents:UIControlEventTouchUpInside];
+ _btnianji = btnianji;
PGJSearchView *viewsearh = [[PGJSearchView alloc] init];
[self.navigationView addSubview:viewsearh];
@@ -139,6 +161,8 @@
make.edges.equalTo(viewsearh);
}];
+
+
UIButton *btsearch = [[UIButton alloc] init];
[viewsearh addSubview:btsearch];
[btsearch mas_makeConstraints:^(MASConstraintMaker *make) {
@@ -146,14 +170,54 @@
}];
[btsearch setTag:1];
[btsearch addTarget:self action:@selector(topAction:) forControlEvents:UIControlEventTouchUpInside];
-
+ [self updateBtShow];
+}
+-(void)updateBtShow
+{
+ if([UserInfoModel shareModel].identityType.intValue==3)
+ {
+ [self.btnianji setTitle:[NSString stringWithFormat:@"%@",[UserInfoModel getidentityTypeString]] forState:UIControlStateNormal];
+ }
+ else
+ {
+ if([UserInfoModel shareModel].user_stage==nil)
+ {
+ [self.btnianji setTitle:@"请选择" forState:UIControlStateNormal];
+ }
+ else
+ {
+ if([UserInfoModel shareModel].isAllLevel.intValue==1)
+ {
+ [self.btnianji setTitle:[NSString stringWithFormat:@"%@-%@",[UserInfoModel getidentityTypeString],[UserInfoModel shareModel].user_stage.parent_name] forState:UIControlStateNormal];
+ }
+ else
+ {
+ [self.btnianji setTitle:[NSString stringWithFormat:@"%@-%@",[UserInfoModel getidentityTypeString],[UserInfoModel shareModel].user_stage.name] forState:UIControlStateNormal];
+ }
+ }
+ }
+ float f_width = [Tools getWidthWithText:self.btnianji.titleLabel.text height:20 font:16]+20;
+ [self.btnianji mas_updateConstraints:^(MASConstraintMaker *make) {
+ make.width.offset(f_width);
+ }];
+ [self.btnianji setIconInRightWithSpacing:6];
}
-(void)topAction:(UIButton *)sender
{
if(sender.tag==0)
{
-
+ ///未选择过身份或者是职场
+ if([UserInfoModel shareModel].identityType.intValue == 3 || [UserInfoModel shareModel].user_stage==nil)
+ {
+ ShouYeLingYuViewController *vc = [ShouYeLingYuViewController new];
+ [self.navigationController pushViewController:vc animated:YES];
+ }
+ else
+ {
+ ShouYeShenFenViewController *vc = [ShouYeShenFenViewController new];
+ [self.navigationController pushViewController:vc animated:YES];
+ }
}
else
{
@@ -193,8 +257,39 @@
#pragma mark -/** 点击图片回调 */
- (void)cycleScrollView:(SDCycleScrollView *)cycleScrollView didSelectItemAtIndex:(NSInteger)index
{
-
+ [self pushAction:index];
}
+-(void)pushAction:(NSInteger)index
+{
+ NSArray *arrbanner = [[UserInfoModel shareModel].config objectForKey:@"client.home.banner.urls"];
+
+ NSDictionary *dic = arrbanner[index];
+ NSString *strpage = [Tools isStringnil:[dic objectForKey:@"page"]];
+ if([strpage isEqualToString:@"recharge"])
+ {
+ StartPayViewController *vc = [StartPayViewController new];
+ vc.ptype = 1;
+ [self.navigationController pushViewController:vc animated:YES];
+ }
+ else if([strpage isEqualToString:@"composition"])
+ {
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"carnumberChangeNotifi" object:@"1"];
+ }
+ else if([strpage isEqualToString:@"write_tools"])
+ {
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"carnumberChangeNotifi" object:@"3"];
+ }
+ else if([strpage isEqualToString:@"classroom_write"])
+ {
+ KeTangXieZuoViewController *vc = [KeTangXieZuoViewController new];
+ [self.navigationController pushViewController:vc animated:YES];
+ }
+ else if([strpage isEqualToString:@"write_assistant"])
+ {
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"carnumberChangeNotifi" object:@"2"];
+ }
+}
+
#pragma mark - custom delegate - 自定义或者第三方控件的delegate
- (NSInteger)numberOfChildViewControllers {
return self.arrdata.count;
diff --git a/ProductApp/ProductApp/ProductMain/工具/图文转换/view/GongJuImageToTextView.m b/ProductApp/ProductApp/ProductMain/工具/图文转换/view/GongJuImageToTextView.m
index 2eee88e..b278384 100644
--- a/ProductApp/ProductApp/ProductMain/工具/图文转换/view/GongJuImageToTextView.m
+++ b/ProductApp/ProductApp/ProductMain/工具/图文转换/view/GongJuImageToTextView.m
@@ -9,6 +9,8 @@
#import "UIView+XLExtension.h"
#import "UIAlertController+Blocks.h"
#import "WYCamaImageTools.h"
+#import "PublicUploadImageManager.h"
+#import "NetWorkManager.h"
@interface GongJuImageToTextView ()
///
@@ -23,6 +25,9 @@
@property (nonatomic, strong) WYCamaImageTools *tools;
+///
+@property (nonatomic , strong) UIImage *imageshow;
+
@end
@implementation GongJuImageToTextView
@@ -293,17 +298,54 @@
}
-(void)delAction
{
+ self.imageshow = nil;
[self.imgvshow setHidden:YES];
}
-(void)bottomAction
{
- [self.viewout setHidden:NO];
-
-
-
+ if(self.imageshow==nil)
+ {
+ [HXHud showMessage:@"请添加图片" afterDelayType:0];
+ return;
+ }
+ [LoadAlterView show];
+ [[PublicUploadImageManager shareManager] netWorkUrlUserPost:[NSString stringWithFormat:@"%@%@",BaseUrl,@"api/file/ocr"] UploadImage:self.imageshow dicQuery:@{} Callback:^(BOOL state, NSDictionary *responseObject, NSString * _Nullable describle) {
+ if(state)
+ {
+ [self getData:[responseObject objectForKey:@"id"]];
+ }
+ else
+ {
+ [LoadAlterView dismiss];
+ [HXHud showMessage:describle afterDelayType:1];
+ }
+ }];
}
+-(void)getData:(NSString *)strid
+{
+ [self addtoolsNumber];
+ [NetWorkManager requestApiOcrData:self ID:strid Callback:^(BOOL state, OcrModel *responseObject, NSString * _Nullable describle) {
+ [LoadAlterView dismiss];
+ if(state)
+ {
+ [self.viewout setHidden:NO];
+ self.lbcontent.text = responseObject.data.result;
+ }
+ else
+ {
+ [HXHud showMessage:responseObject.message afterDelayType:1];
+ }
+ }];
+
+}
+-(void)addtoolsNumber
+{
+ [NetWorkManager requestToolsAdd_countData:self tool_id:[NSString stringWithFormat:@"%d",1] Callback:^(BOOL state, id _Nullable responseObject, NSString * _Nullable describle) {
+
+ }];
+}
-(void)didEndChoosePic:(UIImage *)image
{
UIImage *imagetemp = image;
@@ -327,6 +369,7 @@
}
- (void)uploadImgData:(UIImage *)image {
+ self.imageshow = image;
self.imgvshow.image = image;
[self.imgvshow setHidden:NO];
// [HXLoadingHUD showWithStatus:@"" maskType:0];
diff --git a/ProductApp/ProductApp/ProductMain/工具/搜索/搜索列表/GongJuSearchListViewController.m b/ProductApp/ProductApp/ProductMain/工具/搜索/搜索列表/GongJuSearchListViewController.m
index 00615c6..440d31e 100644
--- a/ProductApp/ProductApp/ProductMain/工具/搜索/搜索列表/GongJuSearchListViewController.m
+++ b/ProductApp/ProductApp/ProductMain/工具/搜索/搜索列表/GongJuSearchListViewController.m
@@ -11,6 +11,15 @@
#import "GongJuListTableViewCell.h"
#import "NetWorkManager.h"
+#import "GongJuCreateViewController.h"
+#import "KeTangXieZuoViewController.h"
+#import "GongJuTextViewController.h"
+#import "GongJuWenBenViewController.h"
+#import "GongJuFanYiViewController.h"
+#import "GongJuImageToTextViewController.h"
+#import "GongJuMinGanCiController.h"
+
+
@interface GongJuSearchListViewController ()
///
@property (nonatomic , strong) UITableView *tableView;
@@ -191,7 +200,48 @@
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
-
+ ToolsListModelDataItems *model = self.arrdata[indexPath.row];
+ if(model.ID.intValue>=1 && model.ID.intValue<=9)
+ {
+ if(model.ID.intValue==9)
+ {
+ KeTangXieZuoViewController *vc = [KeTangXieZuoViewController new];
+ [self.navigationController pushViewController:vc animated:YES];
+ }
+ else if(model.ID.intValue==6||model.ID.intValue==7||model.ID.intValue==8||model.ID.intValue==3)
+ {
+ GongJuTextViewController *vc = [GongJuTextViewController new];
+ vc.strtitle = model.tool_name;
+ vc.type = model.ID.intValue;
+ [self.navigationController pushViewController:vc animated:YES];
+ }
+ else if(model.ID.intValue==5)
+ {
+ GongJuMinGanCiController *vc = [GongJuMinGanCiController new];
+ [self.navigationController pushViewController:vc animated:YES];
+ }
+ else if(model.ID.intValue==4)
+ {
+ GongJuFanYiViewController *vc = [GongJuFanYiViewController new];
+ [self.navigationController pushViewController:vc animated:YES];
+ }
+ else if(model.ID.intValue==2)
+ {
+ GongJuWenBenViewController *vc = [GongJuWenBenViewController new];
+ [self.navigationController pushViewController:vc animated:YES];
+ }
+ else if(model.ID.intValue==1)
+ {
+ GongJuImageToTextViewController *vc = [GongJuImageToTextViewController new];
+ [self.navigationController pushViewController:vc animated:YES];
+ }
+ }
+ else
+ {
+ GongJuCreateViewController *vc = [GongJuCreateViewController new];
+ vc.modelDetail = model;
+ [self.navigationController pushViewController:vc animated:YES];
+ }
}
@end
diff --git a/ProductApp/ProductApp/ProductMain/工具/敏感词/view/GongJuMinGanCiView.m b/ProductApp/ProductApp/ProductMain/工具/敏感词/view/GongJuMinGanCiView.m
index f0edefa..61f69b7 100644
--- a/ProductApp/ProductApp/ProductMain/工具/敏感词/view/GongJuMinGanCiView.m
+++ b/ProductApp/ProductApp/ProductMain/工具/敏感词/view/GongJuMinGanCiView.m
@@ -398,8 +398,9 @@
[LoadAlterView dismiss];
if(state)
{
+ [self.viewout setHidden:NO];
self.lbcontent.isCloseAnimation = YES;
- self.lbcontent.strValue = responseObject.data;
+ self.lbcontent.strValue = [responseObject.data componentsJoinedByString:@"、"];
}
else
{
diff --git a/ProductApp/ProductApp/ProductMain/工具/文本工具/view/GongJuTextView.m b/ProductApp/ProductApp/ProductMain/工具/文本工具/view/GongJuTextView.m
index e6cc205..4a4de0c 100644
--- a/ProductApp/ProductApp/ProductMain/工具/文本工具/view/GongJuTextView.m
+++ b/ProductApp/ProductApp/ProductMain/工具/文本工具/view/GongJuTextView.m
@@ -30,6 +30,7 @@
@property (nonatomic , strong) SSEConfigModel *modelConfig;
@property (nonatomic , assign) BOOL isDragging;
+@property (nonatomic , assign) BOOL isDraw;
@end
@implementation GongJuTextView
@@ -326,6 +327,11 @@
}
-(void)bottomAction
{
+ if(self.isDraw==YES)
+ {
+ [HXHud showMessage:@"请等待处理完成" afterDelayType:0];
+ return;
+ }
if(self.textview.text.length==0)
{
[HXHud showMessage:self.textview.placeholder afterDelayType:0];
@@ -343,6 +349,7 @@
-(void)getdata
{
+ self.isDraw = YES;
[LoadAlterView show];
///translate 翻译、get_summary 摘要、get_keyword 关键字、to_sort 缩写、to_long 扩写
/*
@@ -387,6 +394,7 @@
} error:^(NSString * _Nonnull errorString) {
[LoadAlterView dismiss];
[HXHud showMessage:errorString afterDelayType:0];
+ self.isDraw = NO;
} ID:^(NSString * _Nonnull value) {
}];
@@ -396,6 +404,7 @@
NSLog(@"%@",value);
if(isfinish)
{
+ self.isDraw = NO;
self.lbcontent.isWriteEnd = YES;
}
else
diff --git a/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/DiffMatchPatch.h b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/DiffMatchPatch.h
new file mode 100755
index 0000000..220e5ae
--- /dev/null
+++ b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/DiffMatchPatch.h
@@ -0,0 +1,173 @@
+/*
+ * Diff Match and Patch
+ * Copyright 2018 The diff-match-patch Authors.
+ * https://github.com/google/diff-match-patch
+ *
+ * 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.
+ *
+ * Author: fraser@google.com (Neil Fraser)
+ * ObjC port: jan@geheimwerk.de (Jan Weiß)
+ */
+
+#import
+
+/*
+ * Functions for diff, match and patch.
+ * Computes the difference between two texts to create a patch.
+ * Applies the patch onto another text, allowing for errors.
+ */
+
+/*
+ * The data structure representing a diff is an NSMutableArray of Diff objects:
+ * {Diff(Operation.DIFF_DELETE, "Hello"),
+ * Diff(Operation.DIFF_INSERT, "Goodbye"),
+ * Diff(Operation.DIFF_EQUAL, " world.")}
+ * which means: delete "Hello", add "Goodbye" and keep " world."
+ */
+
+typedef enum {
+ DIFF_DELETE = 1,
+ DIFF_INSERT = 2,
+ DIFF_EQUAL = 3
+} Operation;
+
+
+/*
+ * Class representing one diff operation.
+ */
+@interface Diff : NSObject {
+ Operation operation; // One of: DIFF_INSERT, DIFF_DELETE or DIFF_EQUAL.
+ NSString *text; // The text associated with this diff operation.
+}
+
+@property (nonatomic, assign) Operation operation;
+@property (nonatomic, copy) NSString *text;
+
++ (id)diffWithOperation:(Operation)anOperation andText:(NSString *)aText;
+
+- (id)initWithOperation:(Operation)anOperation andText:(NSString *)aText;
+
+@end
+
+/*
+ * Class representing one patch operation.
+ */
+@interface Patch : NSObject {
+ NSMutableArray *diffs;
+ NSUInteger start1;
+ NSUInteger start2;
+ NSUInteger length1;
+ NSUInteger length2;
+}
+
+@property (nonatomic, retain) NSMutableArray *diffs;
+@property (nonatomic, assign) NSUInteger start1;
+@property (nonatomic, assign) NSUInteger start2;
+@property (nonatomic, assign) NSUInteger length1;
+@property (nonatomic, assign) NSUInteger length2;
+
+@end
+
+
+/*
+ * Class containing the diff, match and patch methods.
+ * Also Contains the behaviour settings.
+ */
+@interface DiffMatchPatch : NSObject {
+ // Number of seconds to map a diff before giving up (0 for infinity).
+ NSTimeInterval Diff_Timeout;
+
+ // Cost of an empty edit operation in terms of edit characters.
+ NSUInteger Diff_EditCost;
+
+ // At what point is no match declared (0.0 = perfection, 1.0 = very loose).
+ double Match_Threshold;
+
+ // How far to search for a match (0 = exact location, 1000+ = broad match).
+ // A match this many characters away from the expected location will add
+ // 1.0 to the score (0.0 is a perfect match).
+ NSInteger Match_Distance;
+
+ // When deleting a large block of text (over ~64 characters), how close
+ // do the contents have to be to match the expected contents. (0.0 =
+ // perfection, 1.0 = very loose). Note that Match_Threshold controls
+ // how closely the end points of a delete need to match.
+ float Patch_DeleteThreshold;
+
+ // Chunk size for context length.
+ uint16_t Patch_Margin;
+
+ // The number of bits in an int.
+ NSUInteger Match_MaxBits;
+}
+
+@property (nonatomic, assign) NSTimeInterval Diff_Timeout;
+@property (nonatomic, assign) NSUInteger Diff_EditCost;
+@property (nonatomic, assign) double Match_Threshold;
+@property (nonatomic, assign) NSInteger Match_Distance;
+@property (nonatomic, assign) float Patch_DeleteThreshold;
+@property (nonatomic, assign) uint16_t Patch_Margin;
+
+- (NSMutableArray *)diff_mainOfOldString:(NSString *)text1 andNewString:(NSString *)text2;
+- (NSMutableArray *)diff_mainOfOldString:(NSString *)text1 andNewString:(NSString *)text2 checkLines:(BOOL)checklines;
+- (NSUInteger)diff_commonPrefixOfFirstString:(NSString *)text1 andSecondString:(NSString *)text2;
+- (NSUInteger)diff_commonSuffixOfFirstString:(NSString *)text1 andSecondString:(NSString *)text2;
+- (void)diff_cleanupSemantic:(NSMutableArray *)diffs;
+- (void)diff_cleanupSemanticLossless:(NSMutableArray *)diffs;
+- (void)diff_cleanupEfficiency:(NSMutableArray *)diffs;
+- (void)diff_cleanupMerge:(NSMutableArray *)diffs;
+- (NSUInteger)diff_xIndexIn:(NSMutableArray *)diffs location:(NSUInteger) loc;
+- (NSString *)diff_prettyHtml:(NSMutableArray *)diffs;
+- (NSString *)diff_text1:(NSMutableArray *)diffs;
+- (NSString *)diff_text2:(NSMutableArray *)diffs;
+- (NSUInteger)diff_levenshtein:(NSMutableArray *)diffs;
+- (NSString *)diff_toDelta:(NSMutableArray *)diffs;
+- (NSMutableArray *)diff_fromDeltaWithText:(NSString *)text1 andDelta:(NSString *)delta error:(NSError **)error;
+
+- (NSUInteger)match_mainForText:(NSString *)text pattern:(NSString *)pattern near:(NSUInteger)loc;
+- (NSMutableDictionary *)match_alphabet:(NSString *)pattern;
+
+- (NSMutableArray *)patch_makeFromOldString:(NSString *)text1 andNewString:(NSString *)text2;
+- (NSMutableArray *)patch_makeFromDiffs:(NSMutableArray *)diffs;
+- (NSMutableArray *)patch_makeFromOldString:(NSString *)text1 newString:(NSString *)text2 diffs:(NSMutableArray *)diffs;
+- (NSMutableArray *)patch_makeFromOldString:(NSString *)text1 andDiffs:(NSMutableArray *)diffs;
+- (NSMutableArray *)patch_deepCopy:(NSArray *)patches; // Copy rule applies!
+- (NSArray *)patch_apply:(NSArray *)sourcePatches toString:(NSString *)text;
+- (NSString *)patch_addPadding:(NSMutableArray *)patches;
+- (void)patch_splitMax:(NSMutableArray *)patches;
+- (NSString *)patch_toText:(NSMutableArray *)patches;
+- (NSMutableArray *)patch_fromText:(NSString *)textline error:(NSError **)error;
+
+@end
+
+
+@interface DiffMatchPatch (PrivateMethods)
+
+- (NSMutableArray *)diff_mainOfOldString:(NSString *)text1 andNewString:(NSString *)text2 checkLines:(BOOL)checklines deadline:(NSTimeInterval)deadline;
+- (NSMutableArray *)diff_computeFromOldString:(NSString *)text1 andNewString:(NSString *)text2 checkLines:(BOOL)checklines deadline:(NSTimeInterval)deadline;
+- (NSMutableArray *)diff_lineModeFromOldString:(NSString *)text1 andNewString:(NSString *)text2 deadline:(NSTimeInterval)deadline;
+- (NSArray *)diff_linesToCharsForFirstString:(NSString *)text1 andSecondString:(NSString *)text2;
+- (void)diff_chars:(NSArray *)diffs toLines:(NSMutableArray *)lineArray;
+- (NSMutableArray *)diff_bisectOfOldString:(NSString *)text1 andNewString:(NSString *)text2 deadline:(NSTimeInterval)deadline;
+- (NSMutableArray *)diff_bisectSplitOfOldString:(NSString *)text1 andNewString:(NSString *)text2 x:(NSUInteger)x y:(NSUInteger)y deadline:(NSTimeInterval)deadline;
+- (NSUInteger)diff_commonOverlapOfFirstString:(NSString *)text1 andSecondString:(NSString *)text2;
+- (NSArray *)diff_halfMatchOfFirstString:(NSString *)text1 andSecondString:(NSString *)text2;
+- (NSArray *)diff_halfMatchIOfLongString:(NSString *)longtext andShortString:(NSString *)shorttext;
+- (NSInteger)diff_cleanupSemanticScoreOfFirstString:(NSString *)one andSecondString:(NSString *)two;
+
+- (NSUInteger)match_bitapOfText:(NSString *)text andPattern:(NSString *)pattern near:(NSUInteger)loc;
+- (double)match_bitapScoreForErrorCount:(NSUInteger)e location:(NSUInteger)x near:(NSUInteger)loc pattern:(NSString *)pattern;
+
+- (void)patch_addContextToPatch:(Patch *)patch sourceText:(NSString *)text;
+
+@end
diff --git a/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/DiffMatchPatch.m b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/DiffMatchPatch.m
new file mode 100755
index 0000000..580f265
--- /dev/null
+++ b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/DiffMatchPatch.m
@@ -0,0 +1,2548 @@
+/*
+ * Diff Match and Patch
+ * Copyright 2018 The diff-match-patch Authors.
+ * https://github.com/google/diff-match-patch
+ *
+ * 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.
+ *
+ * Author: fraser@google.com (Neil Fraser)
+ * ObjC port: jan@geheimwerk.de (Jan Weiß)
+ */
+
+#import "DiffMatchPatch.h"
+
+#import "NSString+JavaSubstring.h"
+#import "NSString+UriCompatibility.h"
+#import "NSMutableDictionary+DMPExtensions.h"
+#import "DiffMatchPatchCFUtilities.h"
+
+
+#if !defined(MAX_OF_CONST_AND_DIFF)
+ // Determines the maximum of two expressions:
+ // The first is a constant (first parameter) while the second expression is
+ // the difference between the second and third parameter. The way this is
+ // calculated prevents integer overflow in the result of the difference.
+ #define MAX_OF_CONST_AND_DIFF(A,B,C) ((B) <= (C) ? (A) : (B) - (C) + (A))
+#endif
+
+
+// JavaScript-style splice function
+void splice(NSMutableArray *input, NSUInteger start, NSUInteger count, NSArray *objects);
+
+/* NSMutableArray * */ void splice(NSMutableArray *input, NSUInteger start, NSUInteger count, NSArray *objects) {
+ NSRange deletionRange = NSMakeRange(start, count);
+ if (objects == nil) {
+ [input removeObjectsInRange:deletionRange];
+ } else {
+ [input replaceObjectsInRange:deletionRange withObjectsFromArray:objects];
+ }
+}
+
+@implementation Diff
+
+@synthesize operation;
+@synthesize text;
+
+/**
+ * Constructor. Initializes the diff with the provided values.
+ * @param anOperation One of DIFF_INSERT, DIFF_DELETE or DIFF_EQUAL.
+ * @param aText The text being applied.
+ */
++ (id)diffWithOperation:(Operation)anOperation
+ andText:(NSString *)aText;
+{
+ return [[[self alloc] initWithOperation:anOperation andText:aText] autorelease];
+}
+
+- (id)initWithOperation:(Operation)anOperation
+ andText:(NSString *)aText;
+{
+ self = [super init];
+ if (self) {
+ self.operation = anOperation;
+ self.text = aText;
+ }
+ return self;
+
+}
+
+- (void)dealloc
+{
+ self.text = nil;
+
+ [super dealloc];
+}
+
+- (id)copyWithZone:(NSZone *)zone
+{
+ id newDiff = [[[self class] allocWithZone:zone]
+ initWithOperation:self.operation
+ andText:self.text];
+
+ return newDiff;
+}
+
+
+/**
+ * Display a human-readable version of this Diff.
+ * @return text version.
+ */
+- (NSString *)description
+{
+ NSString *prettyText = [self.text stringByReplacingOccurrencesOfString:@"\n" withString:@"\u00b6"];
+ NSString *operationName = nil;
+ switch (self.operation) {
+ case DIFF_DELETE:
+ operationName = @"DIFF_DELETE";
+ break;
+ case DIFF_INSERT:
+ operationName = @"DIFF_INSERT";
+ break;
+ case DIFF_EQUAL:
+ operationName = @"DIFF_EQUAL";
+ break;
+ default:
+ break;
+ }
+
+ return [NSString stringWithFormat:@"Diff(%@,\"%@\")", operationName, prettyText];
+}
+
+/**
+ * Is this Diff equivalent to another Diff?
+ * @param obj Another Diff to compare against.
+ * @return YES or NO.
+ */
+- (BOOL)isEqual:(id)obj
+{
+ // If parameter is nil return NO.
+ if (obj == nil) {
+ return NO;
+ }
+
+ // If parameter cannot be cast to Diff return NO.
+ if (![obj isKindOfClass:[Diff class]]) {
+ return NO;
+ }
+
+ // Return YES if the fields match.
+ Diff *p = (Diff *)obj;
+ return p.operation == self.operation && [p.text isEqualToString:self.text];
+}
+
+- (BOOL)isEqualToDiff:(Diff *)obj
+{
+ // If parameter is nil return NO.
+ if (obj == nil) {
+ return NO;
+ }
+
+ // Return YES if the fields match.
+ return obj.operation == self.operation && [obj.text isEqualToString:self.text];
+}
+
+- (NSUInteger)hash
+{
+ return ([text hash] ^ (NSUInteger)operation);
+}
+
+@end
+
+
+@implementation Patch
+
+@synthesize diffs;
+@synthesize start1;
+@synthesize start2;
+@synthesize length1;
+@synthesize length2;
+
+- (id)init
+{
+ self = [super init];
+
+ if (self) {
+ self.diffs = [NSMutableArray array];
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ self.diffs = nil;
+
+ [super dealloc];
+}
+
+- (id)copyWithZone:(NSZone *)zone
+{
+ Patch *newPatch = [[[self class] allocWithZone:zone] init];
+
+ newPatch.diffs = [[NSMutableArray alloc] initWithArray:self.diffs copyItems:YES];
+ newPatch.start1 = self.start1;
+ newPatch.start2 = self.start2;
+ newPatch.length1 = self.length1;
+ newPatch.length2 = self.length2;
+
+ return newPatch;
+}
+
+
+/**
+ * Emulate GNU diff's format.
+ * Header: @@ -382,8 +481,9 @@
+ * Indices are printed as 1-based, not 0-based.
+ * @return The GNU diff NSString.
+ */
+- (NSString *)description
+{
+ NSString *coords1;
+ NSString *coords2;
+
+ if (self.length1 == 0) {
+ coords1 = [NSString stringWithFormat:@"%lu,0",
+ (unsigned long)self.start1];
+ } else if (self.length1 == 1) {
+ coords1 = [NSString stringWithFormat:@"%lu",
+ (unsigned long)self.start1 + 1];
+ } else {
+ coords1 = [NSString stringWithFormat:@"%lu,%lu",
+ (unsigned long)self.start1 + 1, (unsigned long)self.length1];
+ }
+ if (self.length2 == 0) {
+ coords2 = [NSString stringWithFormat:@"%lu,0",
+ (unsigned long)self.start2];
+ } else if (self.length2 == 1) {
+ coords2 = [NSString stringWithFormat:@"%lu",
+ (unsigned long)self.start2 + 1];
+ } else {
+ coords2 = [NSString stringWithFormat:@"%lu,%lu",
+ (unsigned long)self.start2 + 1, (unsigned long)self.length2];
+ }
+
+ NSMutableString *text = [NSMutableString stringWithFormat:@"@@ -%@ +%@ @@\n",
+ coords1, coords2];
+ // Escape the body of the patch with %xx notation.
+ for (Diff *aDiff in self.diffs) {
+ switch (aDiff.operation) {
+ case DIFF_INSERT:
+ [text appendString:@"+"];
+ break;
+ case DIFF_DELETE:
+ [text appendString:@"-"];
+ break;
+ case DIFF_EQUAL:
+ [text appendString:@" "];
+ break;
+ }
+
+ [text appendString:[aDiff.text diff_stringByAddingPercentEscapesForEncodeUriCompatibility]];
+ [text appendString:@"\n"];
+ }
+
+ return text;
+}
+
+@end
+
+
+@implementation DiffMatchPatch
+
+@synthesize Diff_Timeout;
+@synthesize Diff_EditCost;
+@synthesize Match_Threshold;
+@synthesize Match_Distance;
+@synthesize Patch_DeleteThreshold;
+@synthesize Patch_Margin;
+
+- (id)init
+{
+ self = [super init];
+
+ if (self) {
+ Diff_Timeout = 1.0f;
+ Diff_EditCost = 4;
+ Match_Threshold = 0.5f;
+ Match_Distance = 1000;
+ Patch_DeleteThreshold = 0.5f;
+ Patch_Margin = 4;
+
+ Match_MaxBits = 32;
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [super dealloc];
+}
+
+
+#pragma mark Diff Functions
+// DIFF FUNCTIONS
+
+
+/**
+ * Find the differences between two texts.
+ * Run a faster, slightly less optimal diff.
+ * This method allows the 'checklines' of diff_main() to be optional.
+ * Most of the time checklines is wanted, so default to YES.
+ * @param text1 Old NSString to be diffed.
+ * @param text2 New NSString to be diffed.
+ * @return NSMutableArray of Diff objects.
+ */
+- (NSMutableArray *)diff_mainOfOldString:(NSString *)text1
+ andNewString:(NSString *)text2;
+{
+ return [self diff_mainOfOldString:text1 andNewString:text2 checkLines:YES];
+}
+
+/**
+ * Find the differences between two texts.
+ * @param text1 Old string to be diffed.
+ * @param text2 New string to be diffed.
+ * @param checklines Speedup flag. If NO, then don't run a
+ * line-level diff first to identify the changed areas.
+ * If YES, then run a faster slightly less optimal diff.
+ * @return NSMutableArray of Diff objects.
+ */
+- (NSMutableArray *)diff_mainOfOldString:(NSString *)text1
+ andNewString:(NSString *)text2
+ checkLines:(BOOL)checklines;
+{
+ // Set a deadline by which time the diff must be complete.
+ NSTimeInterval deadline;
+ if (Diff_Timeout <= 0) {
+ deadline = [[NSDate distantFuture] timeIntervalSinceReferenceDate];
+ } else {
+ deadline = [[NSDate dateWithTimeIntervalSinceNow:Diff_Timeout] timeIntervalSinceReferenceDate];
+ }
+ return [self diff_mainOfOldString:text1 andNewString:text2 checkLines:YES deadline:deadline];
+}
+
+/**
+ * Find the differences between two texts. Simplifies the problem by
+ * stripping any common prefix or suffix off the texts before diffing.
+ * @param text1 Old NSString to be diffed.
+ * @param text2 New NSString to be diffed.
+ * @param checklines Speedup flag. If NO, then don't run a
+ * line-level diff first to identify the changed areas.
+ * If YES, then run a faster slightly less optimal diff
+ * @param deadline Time when the diff should be complete by. Used
+ * internally for recursive calls. Users should set DiffTimeout
+ * instead.
+ * @return NSMutableArray of Diff objects.
+ */
+- (NSMutableArray *)diff_mainOfOldString:(NSString *)text1
+ andNewString:(NSString *)text2
+ checkLines:(BOOL)checklines
+ deadline:(NSTimeInterval)deadline;
+{
+ // Check for null inputs.
+ if (text1 == nil || text2 == nil) {
+ NSLog(@"Null inputs. (diff_main)");
+ return nil;
+ }
+
+ // Check for equality (speedup).
+ NSMutableArray *diffs;
+ if ([text1 isEqualToString:text2]) {
+ diffs = [NSMutableArray array];
+ if (text1.length != 0) {
+ [diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:text1]];
+ }
+ return diffs;
+ }
+
+ // Trim off common prefix (speedup).
+ NSUInteger commonlength = (NSUInteger)diff_commonPrefix((CFStringRef)text1, (CFStringRef)text2);
+ NSString *commonprefix = [text1 substringWithRange:NSMakeRange(0, commonlength)];
+ text1 = [text1 substringFromIndex:commonlength];
+ text2 = [text2 substringFromIndex:commonlength];
+
+ // Trim off common suffix (speedup).
+ commonlength = (NSUInteger)diff_commonSuffix((CFStringRef)text1, (CFStringRef)text2);
+ NSString *commonsuffix = [text1 substringFromIndex:text1.length - commonlength];
+ text1 = [text1 substringWithRange:NSMakeRange(0, text1.length - commonlength)];
+ text2 = [text2 substringWithRange:NSMakeRange(0, text2.length - commonlength)];
+
+ // Compute the diff on the middle block.
+ diffs = [self diff_computeFromOldString:text1 andNewString:text2 checkLines:checklines deadline:deadline];
+
+ // Restore the prefix and suffix.
+ if (commonprefix.length != 0) {
+ [diffs insertObject:[Diff diffWithOperation:DIFF_EQUAL andText:commonprefix] atIndex:0];
+ }
+ if (commonsuffix.length != 0) {
+ [diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:commonsuffix]];
+ }
+
+ [self diff_cleanupMerge:diffs];
+ return diffs;
+}
+
+/**
+ * Determine the common prefix of two strings.
+ * @param text1 First string.
+ * @param text2 Second string.
+ * @return The number of characters common to the start of each string.
+ */
+- (NSUInteger)diff_commonPrefixOfFirstString:(NSString *)text1
+ andSecondString:(NSString *)text2;
+{
+ return (NSUInteger)diff_commonPrefix((CFStringRef)text1, (CFStringRef)text2);
+}
+
+/**
+ * Determine the common suffix of two strings.
+ * @param text1 First string.
+ * @param text2 Second string.
+ * @return The number of characters common to the end of each string.
+ */
+- (NSUInteger)diff_commonSuffixOfFirstString:(NSString *)text1
+ andSecondString:(NSString *)text2;
+{
+ return (NSUInteger)diff_commonSuffix((CFStringRef)text1, (CFStringRef)text2);
+}
+
+/**
+ * Determine if the suffix of one CFStringRef is the prefix of another.
+ * @param text1 First NSString.
+ * @param text2 Second NSString.
+ * @return The number of characters common to the end of the first
+ * CFStringRef and the start of the second CFStringRef.
+ */
+- (NSUInteger)diff_commonOverlapOfFirstString:(NSString *)text1
+ andSecondString:(NSString *)text2;
+{
+ return (NSUInteger)diff_commonOverlap((CFStringRef)text1, (CFStringRef)text2);
+}
+
+/**
+ * Do the two texts share a substring which is at least half the length of
+ * the longer text?
+ * This speedup can produce non-minimal diffs.
+ * @param text1 First NSString.
+ * @param text2 Second NSString.
+ * @return Five element String array, containing the prefix of text1, the
+ * suffix of text1, the prefix of text2, the suffix of text2 and the
+ * common middle. Or NULL if there was no match.
+ */
+- (NSArray *)diff_halfMatchOfFirstString:(NSString *)text1
+ andSecondString:(NSString *)text2;
+{
+ return [(NSArray *)diff_halfMatchCreate((CFStringRef)text1, (CFStringRef)text2, Diff_Timeout) autorelease];
+}
+
+/**
+ * Does a substring of shorttext exist within longtext such that the
+ * substring is at least half the length of longtext?
+ * @param longtext Longer NSString.
+ * @param shorttext Shorter NSString.
+ * @param index Start index of quarter length substring within longtext.
+ * @return Five element NSArray, containing the prefix of longtext, the
+ * suffix of longtext, the prefix of shorttext, the suffix of shorttext
+ * and the common middle. Or nil if there was no match.
+ */
+- (NSArray *)diff_halfMatchIOfLongString:(NSString *)longtext
+ andShortString:(NSString *)shorttext
+ index:(NSInteger)index;
+{
+ return [((NSArray *)diff_halfMatchICreate((CFStringRef)longtext, (CFStringRef)shorttext, (CFIndex)index)) autorelease];
+}
+
+/**
+ * Find the differences between two texts. Assumes that the texts do not
+ * have any common prefix or suffix.
+ * @param text1 Old NSString to be diffed.
+ * @param text2 New NSString to be diffed.
+ * @param checklines Speedup flag. If NO, then don't run a
+ * line-level diff first to identify the changed areas.
+ * If YES, then run a faster slightly less optimal diff.
+ * @param deadline Time the diff should be complete by.
+ * @return NSMutableArray of Diff objects.
+ */
+- (NSMutableArray *)diff_computeFromOldString:(NSString *)text1
+ andNewString:(NSString *)text2
+ checkLines:(BOOL)checklines
+ deadline:(NSTimeInterval)deadline;
+{
+ NSMutableArray *diffs = [NSMutableArray array];
+
+ if (text1.length == 0) {
+ // Just add some text (speedup).
+ [diffs addObject:[Diff diffWithOperation:DIFF_INSERT andText:text2]];
+ return diffs;
+ }
+
+ if (text2.length == 0) {
+ // Just delete some text (speedup).
+ [diffs addObject:[Diff diffWithOperation:DIFF_DELETE andText:text1]];
+ return diffs;
+ }
+
+ NSString *longtext = text1.length > text2.length ? text1 : text2;
+ NSString *shorttext = text1.length > text2.length ? text2 : text1;
+ NSUInteger i = [longtext rangeOfString:shorttext].location;
+ if (i != NSNotFound) {
+ // Shorter text is inside the longer text (speedup).
+ Operation op = (text1.length > text2.length) ? DIFF_DELETE : DIFF_INSERT;
+ [diffs addObject:[Diff diffWithOperation:op andText:[longtext substringWithRange:NSMakeRange(0, i)]]];
+ [diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:shorttext]];
+ [diffs addObject:[Diff diffWithOperation:op andText:[longtext substringFromIndex:(i + shorttext.length)]]];
+ return diffs;
+ }
+
+ if (shorttext.length == 1) {
+ // Single character string.
+ // After the previous speedup, the character can't be an equality.
+ [diffs addObject:[Diff diffWithOperation:DIFF_DELETE andText:text1]];
+ [diffs addObject:[Diff diffWithOperation:DIFF_INSERT andText:text2]];
+ return diffs;
+ }
+
+ // Check to see if the problem can be split in two.
+ NSArray *hm = [(NSArray *)diff_halfMatchCreate((CFStringRef)text1, (CFStringRef)text2, Diff_Timeout) autorelease];
+ if (hm != nil) {
+ NSAutoreleasePool *splitPool = [NSAutoreleasePool new];
+ // A half-match was found, sort out the return data.
+ NSString *text1_a = [hm objectAtIndex:0];
+ NSString *text1_b = [hm objectAtIndex:1];
+ NSString *text2_a = [hm objectAtIndex:2];
+ NSString *text2_b = [hm objectAtIndex:3];
+ NSString *mid_common = [hm objectAtIndex:4];
+ // Send both pairs off for separate processing.
+ NSMutableArray *diffs_a = [self diff_mainOfOldString:text1_a andNewString:text2_a checkLines:checklines deadline:deadline];
+ NSMutableArray *diffs_b = [self diff_mainOfOldString:text1_b andNewString:text2_b checkLines:checklines deadline:deadline];
+ // Merge the results.
+ diffs = [diffs_a retain];
+ [diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:mid_common]];
+ [diffs addObjectsFromArray:diffs_b];
+ [splitPool drain];
+ return [diffs autorelease];
+ }
+
+ if (checklines && text1.length > 100 && text2.length > 100) {
+ return [self diff_lineModeFromOldString:text1 andNewString:text2 deadline:deadline];
+ }
+
+ NSAutoreleasePool *bisectPool = [NSAutoreleasePool new];
+ diffs = [self diff_bisectOfOldString:text1 andNewString:text2 deadline:deadline];
+ [diffs retain];
+ [bisectPool drain];
+
+ return [diffs autorelease];
+}
+
+/**
+ * Do a quick line-level diff on both strings, then rediff the parts for
+ * greater accuracy.
+ * This speedup can produce non-minimal diffs.
+ * @param text1 Old NSString to be diffed.
+ * @param text2 New NSString to be diffed.
+ * @param deadline Time when the diff should be complete by.
+ * @return NSMutableArray of Diff objects.
+ */
+- (NSMutableArray *)diff_lineModeFromOldString:(NSString *)text1
+ andNewString:(NSString *)text2
+ deadline:(NSTimeInterval)deadline;
+{
+ // Scan the text on a line-by-line basis first.
+ NSArray *a = [self diff_linesToCharsForFirstString:text1 andSecondString:text2];
+ text1 = (NSString *)[a objectAtIndex:0];
+ text2 = (NSString *)[a objectAtIndex:1];
+ NSMutableArray *linearray = (NSMutableArray *)[a objectAtIndex:2];
+
+ NSAutoreleasePool *recursePool = [NSAutoreleasePool new];
+ NSMutableArray *diffs = [self diff_mainOfOldString:text1 andNewString:text2 checkLines:NO deadline:deadline];
+ [diffs retain];
+ [recursePool drain];
+
+ [diffs autorelease];
+
+ // Convert the diff back to original text.
+ [self diff_chars:diffs toLines:linearray];
+ // Eliminate freak matches (e.g. blank lines)
+ [self diff_cleanupSemantic:diffs];
+
+ // Rediff any Replacement blocks, this time character-by-character.
+ // Add a dummy entry at the end.
+ [diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:@""]];
+ NSUInteger thisPointer = 0;
+ NSUInteger count_delete = 0;
+ NSUInteger count_insert = 0;
+ NSString *text_delete = @"";
+ NSString *text_insert = @"";
+ while (thisPointer < diffs.count) {
+ switch (((Diff *)[diffs objectAtIndex:thisPointer]).operation) {
+ case DIFF_INSERT:
+ count_insert++;
+ text_insert = [text_insert stringByAppendingString:((Diff *)[diffs objectAtIndex:thisPointer]).text];
+ break;
+ case DIFF_DELETE:
+ count_delete++;
+ text_delete = [text_delete stringByAppendingString:((Diff *)[diffs objectAtIndex:thisPointer]).text];
+ break;
+ case DIFF_EQUAL:
+ // Upon reaching an equality, check for prior redundancies.
+ if (count_delete >= 1 && count_insert >= 1) {
+ // Delete the offending records and add the merged ones.
+ NSMutableArray *subDiff = [self diff_mainOfOldString:text_delete andNewString:text_insert checkLines:NO deadline:deadline];
+ [diffs removeObjectsInRange:NSMakeRange(thisPointer - count_delete - count_insert,
+ count_delete + count_insert)];
+ thisPointer = thisPointer - count_delete - count_insert;
+ NSUInteger insertionIndex = thisPointer;
+ for (Diff *thisDiff in subDiff) {
+ [diffs insertObject:thisDiff atIndex:insertionIndex];
+ insertionIndex++;
+ }
+ thisPointer = thisPointer + subDiff.count;
+ }
+ count_insert = 0;
+ count_delete = 0;
+ text_delete = @"";
+ text_insert = @"";
+ break;
+ }
+ thisPointer++;
+ }
+ [diffs removeLastObject]; // Remove the dummy entry at the end.
+
+ return diffs;
+}
+
+/**
+ * Find the 'middle snake' of a diff, split the problem in two
+ * and return the recursively constructed diff.
+ * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
+ * @param _text1 Old string to be diffed.
+ * @param _text2 New string to be diffed.
+ * @param deadline Time at which to bail if not yet complete.
+ * @return NSMutableArray of Diff objects.
+ */
+- (NSMutableArray *)diff_bisectOfOldString:(NSString *)_text1
+ andNewString:(NSString *)_text2
+ deadline:(NSTimeInterval)deadline;
+{
+#define text1CharacterAtIndex(A) text1_chars[(A)]
+#define text2CharacterAtIndex(A) text2_chars[(A)]
+#define freeTextBuffers() if (text1_buffer != NULL) free(text1_buffer);\
+ if (text2_buffer != NULL) free(text2_buffer);
+
+ CFStringRef text1 = (CFStringRef)_text1;
+ CFStringRef text2 = (CFStringRef)_text2;
+
+ // Cache the text lengths to prevent multiple calls.
+ CFIndex text1_length = CFStringGetLength(text1);
+ CFIndex text2_length = CFStringGetLength(text2);
+ CFIndex max_d = (text1_length + text2_length + 1) / 2;
+ CFIndex v_offset = max_d;
+ CFIndex v_length = 2 * max_d;
+ CFIndex v1[v_length];
+ CFIndex v2[v_length];
+ for (CFIndex x = 0; x < v_length; x++) {
+ v1[x] = -1;
+ v2[x] = -1;
+ }
+ v1[v_offset + 1] = 0;
+ v2[v_offset + 1] = 0;
+ CFIndex delta = text1_length - text2_length;
+
+ // Prepare access to chars arrays for text1 (massive speedup).
+ const UniChar *text1_chars;
+ UniChar *text1_buffer = NULL;
+ diff_CFStringPrepareUniCharBuffer(text1, &text1_chars, &text1_buffer, CFRangeMake(0, text1_length));
+
+ // Prepare access to chars arrays for text 2 (massive speedup).
+ const UniChar *text2_chars;
+ UniChar *text2_buffer = NULL;
+ diff_CFStringPrepareUniCharBuffer(text2, &text2_chars, &text2_buffer, CFRangeMake(0, text2_length));
+
+ // If the total number of characters is odd, then the front path will
+ // collide with the reverse path.
+ BOOL front = (delta % 2 != 0);
+ // Offsets for start and end of k loop.
+ // Prevents mapping of space beyond the grid.
+ CFIndex k1start = 0;
+ CFIndex k1end = 0;
+ CFIndex k2start = 0;
+ CFIndex k2end = 0;
+ NSMutableArray *diffs;
+ for (CFIndex d = 0; d < max_d; d++) {
+ // Bail out if deadline is reached.
+ if ([NSDate timeIntervalSinceReferenceDate] > deadline) {
+ break;
+ }
+
+ // Walk the front path one step.
+ for (CFIndex k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {
+ CFIndex k1_offset = v_offset + k1;
+ CFIndex x1;
+ if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) {
+ x1 = v1[k1_offset + 1];
+ } else {
+ x1 = v1[k1_offset - 1] + 1;
+ }
+ CFIndex y1 = x1 - k1;
+ while (x1 < text1_length && y1 < text2_length
+ && text1CharacterAtIndex(x1) == text2CharacterAtIndex(y1)) {
+ x1++;
+ y1++;
+ }
+ v1[k1_offset] = x1;
+ if (x1 > text1_length) {
+ // Ran off the right of the graph.
+ k1end += 2;
+ } else if (y1 > text2_length) {
+ // Ran off the bottom of the graph.
+ k1start += 2;
+ } else if (front) {
+ CFIndex k2_offset = v_offset + delta - k1;
+ if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) {
+ // Mirror x2 onto top-left coordinate system.
+ CFIndex x2 = text1_length - v2[k2_offset];
+ if (x1 >= x2) {
+ freeTextBuffers();
+
+ // Overlap detected.
+ return [self diff_bisectSplitOfOldString:_text1
+ andNewString:_text2
+ x:x1
+ y:y1
+ deadline:deadline];
+ }
+ }
+ }
+ }
+
+ // Walk the reverse path one step.
+ for (CFIndex k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {
+ CFIndex k2_offset = v_offset + k2;
+ CFIndex x2;
+ if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) {
+ x2 = v2[k2_offset + 1];
+ } else {
+ x2 = v2[k2_offset - 1] + 1;
+ }
+ CFIndex y2 = x2 - k2;
+ while (x2 < text1_length && y2 < text2_length
+ && text1CharacterAtIndex(text1_length - x2 - 1)
+ == text2CharacterAtIndex(text2_length - y2 - 1)) {
+ x2++;
+ y2++;
+ }
+ v2[k2_offset] = x2;
+ if (x2 > text1_length) {
+ // Ran off the left of the graph.
+ k2end += 2;
+ } else if (y2 > text2_length) {
+ // Ran off the top of the graph.
+ k2start += 2;
+ } else if (!front) {
+ CFIndex k1_offset = v_offset + delta - k2;
+ if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) {
+ CFIndex x1 = v1[k1_offset];
+ CFIndex y1 = v_offset + x1 - k1_offset;
+ // Mirror x2 onto top-left coordinate system.
+ x2 = text1_length - x2;
+ if (x1 >= x2) {
+ // Overlap detected.
+ freeTextBuffers();
+
+ return [self diff_bisectSplitOfOldString:_text1
+ andNewString:_text2
+ x:x1
+ y:y1
+ deadline:deadline];
+ }
+ }
+ }
+ }
+ }
+
+ freeTextBuffers();
+
+ // Diff took too long and hit the deadline or
+ // number of diffs equals number of characters, no commonality at all.
+ diffs = [NSMutableArray array];
+ [diffs addObject:[Diff diffWithOperation:DIFF_DELETE andText:_text1]];
+ [diffs addObject:[Diff diffWithOperation:DIFF_INSERT andText:_text2]];
+ return diffs;
+
+#undef freeTextBuffers
+#undef text1CharacterAtIndex
+#undef text2CharacterAtIndex
+}
+
+/**
+ * Given the location of the 'middle snake', split the diff in two parts
+ * and recurse.
+ * @param text1 Old string to be diffed.
+ * @param text2 New string to be diffed.
+ * @param x Index of split point in text1.
+ * @param y Index of split point in text2.
+ * @param deadline Time at which to bail if not yet complete.
+ * @return NSMutableArray of Diff objects.
+ */
+- (NSMutableArray *)diff_bisectSplitOfOldString:(NSString *)text1
+ andNewString:(NSString *)text2
+ x:(NSUInteger)x
+ y:(NSUInteger)y
+ deadline:(NSTimeInterval)deadline;
+{
+ NSString *text1a = [text1 substringToIndex:x];
+ NSString *text2a = [text2 substringToIndex:y];
+ NSString *text1b = [text1 substringFromIndex:x];
+ NSString *text2b = [text2 substringFromIndex:y];
+
+ // Compute both diffs serially.
+ NSMutableArray *diffs = [self diff_mainOfOldString:text1a
+ andNewString:text2a
+ checkLines:NO
+ deadline:deadline];
+ NSMutableArray *diffsb = [self diff_mainOfOldString:text1b
+ andNewString:text2b
+ checkLines:NO
+ deadline:deadline];
+
+ [diffs addObjectsFromArray: diffsb];
+ return diffs;
+}
+
+/**
+ * Split two texts into a list of strings. Reduce the texts to a string of
+ * hashes where each Unicode character represents one line.
+ * @param text1 First NSString.
+ * @param text2 Second NSString.
+ * @return Three element NSArray, containing the encoded text1, the
+ * encoded text2 and the NSMutableArray of unique strings. The zeroth element
+ * of the NSArray of unique strings is intentionally blank.
+ */
+- (NSArray *)diff_linesToCharsForFirstString:(NSString *)text1
+ andSecondString:(NSString *)text2;
+{
+ NSMutableArray *lineArray = [NSMutableArray array]; // NSString objects
+ NSMutableDictionary *lineHash = [NSMutableDictionary dictionary]; // keys: NSString, values:NSNumber
+ // e.g. [lineArray objectAtIndex:4] == "Hello\n"
+ // e.g. [lineHash objectForKey:"Hello\n"] == 4
+
+ // "\x00" is a valid character, but various debuggers don't like it.
+ // So we'll insert a junk entry to avoid generating a nil character.
+ [lineArray addObject:@""];
+
+ // Allocate 2/3rds of the space for text1, the rest for text2.
+ NSString *chars1 = (NSString *)diff_linesToCharsMungeCFStringCreate((CFStringRef)text1,
+ (CFMutableArrayRef)lineArray,
+ (CFMutableDictionaryRef)lineHash,
+ 40000);
+ NSString *chars2 = (NSString *)diff_linesToCharsMungeCFStringCreate((CFStringRef)text2,
+ (CFMutableArrayRef)lineArray,
+ (CFMutableDictionaryRef)lineHash,
+ 65535);
+
+ NSArray *result = [NSArray arrayWithObjects:chars1, chars2, lineArray, nil];
+
+ [chars1 release];
+ [chars2 release];
+
+ return result;
+}
+
+/**
+ * Rehydrate the text in a diff from an NSString of line hashes to real lines
+ * of text.
+ * @param diffs NSArray of Diff objects.
+ * @param lineArray NSMutableArray of unique strings.
+ */
+- (void)diff_chars:(NSArray *)diffs toLines:(NSMutableArray *)lineArray;
+{
+ NSMutableString *text;
+ NSUInteger lineHash;
+ for (Diff *diff in diffs) {
+ text = [NSMutableString string];
+ for (NSUInteger j = 0; j < [diff.text length]; j++) {
+ lineHash = (NSUInteger)[diff.text characterAtIndex:j];
+ [text appendString:[lineArray objectAtIndex:lineHash]];
+ }
+ diff.text = text;
+ }
+}
+
+/**
+ * Reorder and merge like edit sections. Merge equalities.
+ * Any edit section can move as long as it doesn't cross an equality.
+ * @param diffs NSMutableArray of Diff objects.
+ */
+- (void)diff_cleanupMerge:(NSMutableArray *)diffs;
+{
+#define prevDiff ((Diff *)[diffs objectAtIndex:(thisPointer - 1)])
+#define thisDiff ((Diff *)[diffs objectAtIndex:thisPointer])
+#define nextDiff ((Diff *)[diffs objectAtIndex:(thisPointer + 1)])
+
+ if (diffs.count == 0) {
+ return;
+ }
+
+ // Add a dummy entry at the end.
+ [diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:@""]];
+ NSUInteger thisPointer = 0;
+ NSUInteger count_delete = 0;
+ NSUInteger count_insert = 0;
+ NSString *text_delete = @"";
+ NSString *text_insert = @"";
+ NSUInteger commonlength;
+ while (thisPointer < diffs.count) {
+ switch (thisDiff.operation) {
+ case DIFF_INSERT:
+ count_insert++;
+ text_insert = [text_insert stringByAppendingString:thisDiff.text];
+ thisPointer++;
+ break;
+ case DIFF_DELETE:
+ count_delete++;
+ text_delete = [text_delete stringByAppendingString:thisDiff.text];
+ thisPointer++;
+ break;
+ case DIFF_EQUAL:
+ // Upon reaching an equality, check for prior redundancies.
+ if (count_delete + count_insert > 1) {
+ if (count_delete != 0 && count_insert != 0) {
+ // Factor out any common prefixes.
+ commonlength = (NSUInteger)diff_commonPrefix((CFStringRef)text_insert, (CFStringRef)text_delete);
+ if (commonlength != 0) {
+ if ((thisPointer - count_delete - count_insert) > 0 &&
+ ((Diff *)[diffs objectAtIndex:(thisPointer - count_delete - count_insert - 1)]).operation
+ == DIFF_EQUAL) {
+ ((Diff *)[diffs objectAtIndex:(thisPointer - count_delete - count_insert - 1)]).text
+ = [((Diff *)[diffs objectAtIndex:(thisPointer - count_delete - count_insert - 1)]).text
+ stringByAppendingString:[text_insert substringWithRange:NSMakeRange(0, commonlength)]];
+ } else {
+ [diffs insertObject:[Diff diffWithOperation:DIFF_EQUAL
+ andText:[text_insert substringWithRange:NSMakeRange(0, commonlength)]]
+ atIndex:0];
+ thisPointer++;
+ }
+ text_insert = [text_insert substringFromIndex:commonlength];
+ text_delete = [text_delete substringFromIndex:commonlength];
+ }
+ // Factor out any common suffixes.
+ commonlength = (NSUInteger)diff_commonSuffix((CFStringRef)text_insert, (CFStringRef)text_delete);
+ if (commonlength != 0) {
+ thisDiff.text = [[text_insert substringFromIndex:(text_insert.length
+ - commonlength)] stringByAppendingString:thisDiff.text];
+ text_insert = [text_insert substringWithRange:NSMakeRange(0,
+ text_insert.length - commonlength)];
+ text_delete = [text_delete substringWithRange:NSMakeRange(0,
+ text_delete.length - commonlength)];
+ }
+ }
+ // Delete the offending records and add the merged ones.
+ thisPointer -= count_delete + count_insert;
+
+ splice(diffs, thisPointer, count_delete + count_insert, nil);
+ if ([text_delete length] != 0) {
+ splice(diffs, thisPointer, 0,
+ [NSMutableArray arrayWithObject:[Diff diffWithOperation:DIFF_DELETE andText:text_delete]]);
+ thisPointer++;
+ }
+ if ([text_insert length] != 0) {
+ splice(diffs, thisPointer, 0,
+ [NSMutableArray arrayWithObject:[Diff diffWithOperation:DIFF_INSERT andText:text_insert]]);
+ thisPointer++;
+ }
+ thisPointer++;
+ } else if (thisPointer != 0 && prevDiff.operation == DIFF_EQUAL) {
+ // Merge this equality with the previous one.
+ prevDiff.text = [prevDiff.text stringByAppendingString:thisDiff.text];
+ [diffs removeObjectAtIndex:thisPointer];
+ } else {
+ thisPointer++;
+ }
+ count_insert = 0;
+ count_delete = 0;
+ text_delete = @"";
+ text_insert = @"";
+ break;
+ }
+ }
+ if (((Diff *)diffs.lastObject).text.length == 0) {
+ [diffs removeLastObject]; // Remove the dummy entry at the end.
+ }
+
+ // Second pass: look for single edits surrounded on both sides by
+ // equalities which can be shifted sideways to eliminate an equality.
+ // e.g: ABAC -> ABAC
+ BOOL changes = NO;
+ thisPointer = 1;
+ // Intentionally ignore the first and last element (don't need checking).
+ while (thisPointer < (diffs.count - 1)) {
+ if (prevDiff.operation == DIFF_EQUAL &&
+ nextDiff.operation == DIFF_EQUAL) {
+ // This is a single edit surrounded by equalities.
+ if ([prevDiff.text length] == 0) {
+ splice(diffs, thisPointer - 1, 1, nil);
+ changes = YES;
+ } else if ([thisDiff.text hasSuffix:prevDiff.text]) {
+ // Shift the edit over the previous equality.
+ thisDiff.text = [prevDiff.text stringByAppendingString:
+ [thisDiff.text substringWithRange:NSMakeRange(0, thisDiff.text.length - prevDiff.text.length)]];
+ nextDiff.text = [prevDiff.text stringByAppendingString:nextDiff.text];
+ splice(diffs, thisPointer - 1, 1, nil);
+ changes = YES;
+ } else if ([thisDiff.text hasPrefix:nextDiff.text]) {
+ // Shift the edit over the next equality.
+ prevDiff.text = [prevDiff.text stringByAppendingString:nextDiff.text];
+ thisDiff.text = [[thisDiff.text substringFromIndex:nextDiff.text.length] stringByAppendingString:nextDiff.text];
+ splice(diffs, thisPointer + 1, 1, nil);
+ changes = YES;
+ }
+ }
+ thisPointer++;
+ }
+ // If shifts were made, the diff needs reordering and another shift sweep.
+ if (changes) {
+ [self diff_cleanupMerge:diffs];
+ }
+
+#undef prevDiff
+#undef thisDiff
+#undef nextDiff
+}
+
+
+/**
+ * Look for single edits surrounded on both sides by equalities
+ * which can be shifted sideways to align the edit to a word boundary.
+ * e.g: The cat came. -> The cat came.
+ * @param diffs NSMutableArray of Diff objects.
+ */
+- (void)diff_cleanupSemanticLossless:(NSMutableArray *)diffs;
+{
+#define prevDiff ((Diff *)[diffs objectAtIndex:(thisPointer - 1)])
+#define thisDiff ((Diff *)[diffs objectAtIndex:thisPointer])
+#define nextDiff ((Diff *)[diffs objectAtIndex:(thisPointer + 1)])
+
+ if (diffs.count == 0) {
+ return;
+ }
+
+ NSUInteger thisPointer = 1;
+ // Intentionally ignore the first and last element (don't need checking).
+ while (thisPointer < (diffs.count - 1)) {
+ if (prevDiff.operation == DIFF_EQUAL && nextDiff.operation == DIFF_EQUAL) {
+ // This is a single edit surrounded by equalities.
+ NSString *equality1 = prevDiff.text;
+ NSString *edit = thisDiff.text;
+ NSString *equality2 = nextDiff.text;
+
+ // First, shift the edit as far left as possible.
+ NSUInteger commonOffset = (NSUInteger)diff_commonSuffix((CFStringRef)equality1, (CFStringRef)edit);
+
+ if (commonOffset > 0) {
+ NSString *commonString = [edit substringFromIndex:(edit.length - commonOffset)];
+ equality1 = [equality1 substringWithRange:NSMakeRange(0, (equality1.length - commonOffset))];
+ edit = [commonString stringByAppendingString:[edit substringWithRange:NSMakeRange(0, (edit.length - commonOffset))]];
+ equality2 = [commonString stringByAppendingString:equality2];
+ }
+
+ // Second, step right character by character,
+ // looking for the best fit.
+ NSString *bestEquality1 = equality1;
+ NSString *bestEdit = edit;
+ NSString *bestEquality2 = equality2;
+ CFIndex bestScore = diff_cleanupSemanticScore((CFStringRef)equality1, (CFStringRef)edit) +
+ diff_cleanupSemanticScore((CFStringRef)edit, (CFStringRef)equality2);
+ while (edit.length != 0 && equality2.length != 0
+ && [edit characterAtIndex:0] == [equality2 characterAtIndex:0]) {
+ equality1 = [equality1 stringByAppendingString:[edit substringWithRange:NSMakeRange(0, 1)]];
+ edit = [[edit substringFromIndex:1] stringByAppendingString:[equality2 substringWithRange:NSMakeRange(0, 1)]];
+ equality2 = [equality2 substringFromIndex:1];
+ CFIndex score = diff_cleanupSemanticScore((CFStringRef)equality1, (CFStringRef)edit) +
+ diff_cleanupSemanticScore((CFStringRef)edit, (CFStringRef)equality2);
+ // The >= encourages trailing rather than leading whitespace on edits.
+ if (score >= bestScore) {
+ bestScore = score;
+ bestEquality1 = equality1;
+ bestEdit = edit;
+ bestEquality2 = equality2;
+ }
+ }
+
+ if (prevDiff.text != bestEquality1) {
+ // We have an improvement, save it back to the diff.
+ if (bestEquality1.length != 0) {
+ prevDiff.text = bestEquality1;
+ } else {
+ [diffs removeObjectAtIndex:thisPointer - 1];
+ thisPointer--;
+ }
+ thisDiff.text = bestEdit;
+ if (bestEquality2.length != 0) {
+ nextDiff.text = bestEquality2;
+ } else {
+ [diffs removeObjectAtIndex:thisPointer + 1];
+ thisPointer--;
+ }
+ }
+ }
+ thisPointer++;
+ }
+
+#undef prevDiff
+#undef thisDiff
+#undef nextDiff
+}
+
+/**
+ * Given two strings, comAdde a score representing whether the internal
+ * boundary falls on logical boundaries.
+ * Scores range from 5 (best) to 0 (worst).
+ * @param one First string.
+ * @param two Second string.
+ * @return The score.
+ */
+- (NSInteger)diff_cleanupSemanticScoreOfFirstString:(NSString *)one
+ andSecondString:(NSString *)two;
+{
+ return diff_cleanupSemanticScore((CFStringRef)one, (CFStringRef)two);
+}
+
+/**
+ * Reduce the number of edits by eliminating operationally trivial
+ * equalities.
+ * @param diffs NSMutableArray of Diff objects.
+ */
+- (void)diff_cleanupEfficiency:(NSMutableArray *)diffs;
+{
+#define thisDiff ((Diff *)[diffs objectAtIndex:thisPointer])
+#define equalitiesLastItem ((NSNumber *)equalities.lastObject)
+#define equalitiesLastValue ((NSNumber *)equalities.lastObject).integerValue
+ if (diffs.count == 0) {
+ return;
+ }
+
+ BOOL changes = NO;
+ // Stack of indices where equalities are found.
+ NSMutableArray *equalities = [NSMutableArray array];
+ // Always equal to equalities.lastObject.text
+ NSString *lastEquality = nil;
+ NSInteger thisPointer = 0; // Index of current position.
+ // Is there an insertion operation before the last equality.
+ BOOL pre_ins = NO;
+ // Is there a deletion operation before the last equality.
+ BOOL pre_del = NO;
+ // Is there an insertion operation after the last equality.
+ BOOL post_ins = NO;
+ // Is there a deletion operation after the last equality.
+ BOOL post_del = NO;
+
+ NSUInteger indexToChange;
+ Diff *diffToChange;
+
+ while (thisPointer < (NSInteger)diffs.count) {
+ if (thisDiff.operation == DIFF_EQUAL) { // Equality found.
+ if (thisDiff.text.length < Diff_EditCost && (post_ins || post_del)) {
+ // Candidate found.
+ [equalities addObject:[NSNumber numberWithInteger:thisPointer]];
+ pre_ins = post_ins;
+ pre_del = post_del;
+ lastEquality = thisDiff.text;
+ } else {
+ // Not a candidate, and can never become one.
+ [equalities removeAllObjects];
+ lastEquality = nil;
+ }
+ post_ins = post_del = NO;
+ } else { // An insertion or deletion.
+ if (thisDiff.operation == DIFF_DELETE) {
+ post_del = YES;
+ } else {
+ post_ins = YES;
+ }
+ /*
+ * Five types to be split:
+ * ABXYCD
+ * AXCD
+ * ABXC
+ * AXCD
+ * ABXC
+ */
+ if (lastEquality != nil
+ && ((pre_ins && pre_del && post_ins && post_del)
+ || ((lastEquality.length < Diff_EditCost / 2)
+ && ((pre_ins ? 1 : 0) + (pre_del ? 1 : 0) + (post_ins ? 1 : 0)
+ + (post_del ? 1 : 0)) == 3))) {
+ // Duplicate record.
+ [diffs insertObject:[Diff diffWithOperation:DIFF_DELETE andText:lastEquality]
+ atIndex:equalitiesLastValue];
+ // Change second copy to insert.
+ // Hash values for objects must not change while in a collection
+ indexToChange = equalitiesLastValue + 1;
+ diffToChange = [[diffs objectAtIndex:indexToChange] retain];
+ [diffs replaceObjectAtIndex:indexToChange withObject:[NSNull null]];
+ diffToChange.operation = DIFF_INSERT;
+ [diffs replaceObjectAtIndex:indexToChange withObject:diffToChange];
+ [diffToChange release];
+
+ [equalities removeLastObject]; // Throw away the equality we just deleted.
+ lastEquality = nil;
+ if (pre_ins && pre_del) {
+ // No changes made which could affect previous entry, keep going.
+ post_ins = post_del = YES;
+ [equalities removeAllObjects];
+ } else {
+ if (equalities.count > 0) {
+ [equalities removeLastObject];
+ }
+
+ thisPointer = equalities.count > 0 ? equalitiesLastValue : -1;
+ post_ins = post_del = NO;
+ }
+ changes = YES;
+ }
+ }
+ thisPointer++;
+ }
+
+ if (changes) {
+ [self diff_cleanupMerge:diffs];
+ }
+
+#undef thisDiff
+#undef equalitiesLastItem
+#undef equalitiesLastValue
+}
+
+/**
+ * Convert a Diff list into a pretty HTML report.
+ * @param diffs NSMutableArray of Diff objects.
+ * @return HTML representation.
+ */
+- (NSString *)diff_prettyHtml:(NSMutableArray *)diffs;
+{
+ NSMutableString *html = [NSMutableString string];
+ for (Diff *aDiff in diffs) {
+ NSMutableString *text = [[aDiff.text mutableCopy] autorelease];
+ [text replaceOccurrencesOfString:@"&" withString:@"&" options:NSLiteralSearch range:NSMakeRange(0, text.length)];
+ [text replaceOccurrencesOfString:@"<" withString:@"<" options:NSLiteralSearch range:NSMakeRange(0, text.length)];
+ [text replaceOccurrencesOfString:@">" withString:@">" options:NSLiteralSearch range:NSMakeRange(0, text.length)];
+ [text replaceOccurrencesOfString:@"\n" withString:@"¶
" options:NSLiteralSearch range:NSMakeRange(0, text.length)];
+
+ switch (aDiff.operation) {
+ case DIFF_INSERT:
+ [html appendFormat:@"%@", text];
+ break;
+ case DIFF_DELETE:
+ [html appendFormat:@"%@", text];
+ break;
+ case DIFF_EQUAL:
+ [html appendFormat:@"%@", text];
+ break;
+ }
+ }
+ return html;
+}
+
+/**
+ * Compute and return the source text (all equalities and deletions).
+ * @param diffs NSMutableArray of Diff objects.
+ * @return Source text.
+ */
+- (NSString *)diff_text1:(NSMutableArray *)diffs;
+{
+ NSMutableString *text = [NSMutableString string];
+ for (Diff *aDiff in diffs) {
+ if (aDiff.operation != DIFF_INSERT) {
+ [text appendString:aDiff.text];
+ }
+ }
+ return text;
+}
+
+/**
+ * Compute and return the destination text (all equalities and insertions).
+ * @param diffs NSMutableArray of Diff objects.
+ * @return Destination text.
+ */
+- (NSString *)diff_text2:(NSMutableArray *)diffs;
+{
+ NSMutableString *text = [NSMutableString string];
+ for (Diff *aDiff in diffs) {
+ if (aDiff.operation != DIFF_DELETE) {
+ [text appendString:aDiff.text];
+ }
+ }
+ return text;
+}
+
+/**
+ * Crush the diff into an encoded NSString which describes the operations
+ * required to transform text1 into text2.
+ * E.g. =3\t-2\t+ing -> Keep 3 chars, delete 2 chars, insert 'ing'.
+ * Operations are tab-separated. Inserted text is escaped using %xx
+ * notation.
+ * @param diffs NSMutableArray of Diff objects.
+ * @return Delta text.
+ */
+- (NSString *)diff_toDelta:(NSMutableArray *)diffs;
+{
+ NSMutableString *delta = [NSMutableString string];
+ for (Diff *aDiff in diffs) {
+ switch (aDiff.operation) {
+ case DIFF_INSERT:
+ [delta appendFormat:@"+%@\t", [[aDiff.text diff_stringByAddingPercentEscapesForEncodeUriCompatibility]
+ stringByReplacingOccurrencesOfString:@"%20" withString:@" "]];
+ break;
+ case DIFF_DELETE:
+ [delta appendFormat:@"-%" PRId32 "\t", (int32_t)aDiff.text.length];
+ break;
+ case DIFF_EQUAL:
+ [delta appendFormat:@"=%" PRId32 "\t", (int32_t)aDiff.text.length];
+ break;
+ }
+ }
+
+ if (delta.length != 0) {
+ // Strip off trailing tab character.
+ return [delta substringWithRange:NSMakeRange(0, delta.length-1)];
+ }
+ return delta;
+}
+
+/**
+ * Given the original text1, and an encoded NSString which describes the
+ * operations required to transform text1 into text2, compute the full diff.
+ * @param text1 Source NSString for the diff.
+ * @param delta Delta text.
+ * @param error NSError if invalid input.
+ * @return NSMutableArray of Diff objects or nil if invalid.
+ */
+- (NSMutableArray *)diff_fromDeltaWithText:(NSString *)text1
+ andDelta:(NSString *)delta
+ error:(NSError **)error;
+{
+ NSMutableArray *diffs = [NSMutableArray array];
+ NSUInteger thisPointer = 0; // Cursor in text1
+ NSArray *tokens = [delta componentsSeparatedByString:@"\t"];
+ NSInteger n;
+ NSDictionary *errorDetail = nil;
+ for (NSString *token in tokens) {
+ if (token.length == 0) {
+ // Blank tokens are ok (from a trailing \t).
+ continue;
+ }
+ // Each token begins with a one character parameter which specifies the
+ // operation of this token (delete, insert, equality).
+ NSString *param = [token substringFromIndex:1];
+ switch ([token characterAtIndex:0]) {
+ case '+':
+ param = [param diff_stringByReplacingPercentEscapesForEncodeUriCompatibility];
+ if (param == nil) {
+ if (error != NULL) {
+ errorDetail = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSString stringWithFormat:NSLocalizedString(@"Invalid character in diff_fromDelta: %@", @"Error"), param],
+ NSLocalizedDescriptionKey, nil];
+ *error = [NSError errorWithDomain:@"DiffMatchPatchErrorDomain" code:99 userInfo:errorDetail];
+ }
+ return nil;
+ }
+ [diffs addObject:[Diff diffWithOperation:DIFF_INSERT andText:param]];
+ break;
+ case '-':
+ // Fall through.
+ case '=':
+ n = [param integerValue];
+ if (n == 0) {
+ if (error != NULL) {
+ errorDetail = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSString stringWithFormat:NSLocalizedString(@"Invalid number in diff_fromDelta: %@", @"Error"), param],
+ NSLocalizedDescriptionKey, nil];
+ *error = [NSError errorWithDomain:@"DiffMatchPatchErrorDomain" code:100 userInfo:errorDetail];
+ }
+ return nil;
+ } else if (n < 0) {
+ if (error != NULL) {
+ errorDetail = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSString stringWithFormat:NSLocalizedString(@"Negative number in diff_fromDelta: %@", @"Error"), param],
+ NSLocalizedDescriptionKey, nil];
+ *error = [NSError errorWithDomain:@"DiffMatchPatchErrorDomain" code:101 userInfo:errorDetail];
+ }
+ return nil;
+ }
+ NSString *text;
+ @try {
+ text = [text1 substringWithRange:NSMakeRange(thisPointer, (NSUInteger)n)];
+ thisPointer += (NSUInteger)n;
+ }
+ @catch (NSException *e) {
+ if (error != NULL) {
+ // CHANGME: Pass on the information contained in e
+ errorDetail = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSString stringWithFormat:NSLocalizedString(@"Delta length (%lu) larger than source text length (%lu).", @"Error"),
+ (unsigned long)thisPointer, (unsigned long)text1.length],
+ NSLocalizedDescriptionKey, nil];
+ *error = [NSError errorWithDomain:@"DiffMatchPatchErrorDomain" code:102 userInfo:errorDetail];
+ }
+ return nil;
+ }
+ if ([token characterAtIndex:0] == '=') {
+ [diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:text]];
+ } else {
+ [diffs addObject:[Diff diffWithOperation:DIFF_DELETE andText:text]];
+ }
+ break;
+ default:
+ // Anything else is an error.
+ if (error != NULL) {
+ errorDetail = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSString stringWithFormat:NSLocalizedString(@"Invalid diff operation in diff_fromDelta: %C", @"Error"),
+ [token characterAtIndex:0]],
+ NSLocalizedDescriptionKey, nil];
+ *error = [NSError errorWithDomain:@"DiffMatchPatchErrorDomain" code:102 userInfo:errorDetail];
+ }
+ return nil;
+ }
+ }
+ if (thisPointer != text1.length) {
+ if (error != NULL) {
+ errorDetail = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSString stringWithFormat:NSLocalizedString(@"Delta length (%lu) smaller than source text length (%lu).", @"Error"),
+ (unsigned long)thisPointer, (unsigned long)text1.length],
+ NSLocalizedDescriptionKey, nil];
+ *error = [NSError errorWithDomain:@"DiffMatchPatchErrorDomain" code:103 userInfo:errorDetail];
+ }
+ return nil;
+ }
+ return diffs;
+}
+
+/**
+ * loc is a location in text1, compute and return the equivalent location in
+ * text2.
+ * e.g. "The cat" vs "The big cat", 1->1, 5->8
+ * @param diffs NSMutableArray of Diff objects.
+ * @param loc Location within text1.
+ * @return Location within text2.
+ */
+- (NSUInteger)diff_xIndexIn:(NSMutableArray *)diffs
+ location:(NSUInteger) loc;
+{
+ NSUInteger chars1 = 0;
+ NSUInteger chars2 = 0;
+ NSUInteger last_chars1 = 0;
+ NSUInteger last_chars2 = 0;
+ Diff *lastDiff = nil;
+ for (Diff *aDiff in diffs) {
+ if (aDiff.operation != DIFF_INSERT) {
+ // Equality or deletion.
+ chars1 += aDiff.text.length;
+ }
+ if (aDiff.operation != DIFF_DELETE) {
+ // Equality or insertion.
+ chars2 += aDiff.text.length;
+ }
+ if (chars1 > loc) {
+ // Overshot the location.
+ lastDiff = aDiff;
+ break;
+ }
+ last_chars1 = chars1;
+ last_chars2 = chars2;
+ }
+ if (lastDiff != nil && lastDiff.operation == DIFF_DELETE) {
+ // The location was deleted.
+ return last_chars2;
+ }
+ // Add the remaining character length.
+ return last_chars2 + (loc - last_chars1);
+}
+
+/**
+ * Compute the Levenshtein distance; the number of inserted, deleted or
+ * substituted characters.
+ * @param diffs NSMutableArray of Diff objects.
+ * @return Number of changes.
+ */
+- (NSUInteger)diff_levenshtein:(NSMutableArray *)diffs;
+{
+ NSUInteger levenshtein = 0;
+ NSUInteger insertions = 0;
+ NSUInteger deletions = 0;
+ for (Diff *aDiff in diffs) {
+ switch (aDiff.operation) {
+ case DIFF_INSERT:
+ insertions += aDiff.text.length;
+ break;
+ case DIFF_DELETE:
+ deletions += aDiff.text.length;
+ break;
+ case DIFF_EQUAL:
+ // A deletion and an insertion is one substitution.
+ levenshtein += MAX(insertions, deletions);
+ insertions = 0;
+ deletions = 0;
+ break;
+ }
+ }
+ levenshtein += MAX(insertions, deletions);
+ return levenshtein;
+}
+
+/**
+ * Reduce the number of edits by eliminating semantically trivial
+ * equalities.
+ * @param diffs NSMutableArray of Diff objects.
+ */
+- (void)diff_cleanupSemantic:(NSMutableArray *)diffs;
+{
+#define prevDiff ((Diff *)[diffs objectAtIndex:(thisPointer - 1)])
+#define thisDiff ((Diff *)[diffs objectAtIndex:thisPointer])
+#define nextDiff ((Diff *)[diffs objectAtIndex:(thisPointer + 1)])
+#define equalitiesLastItem ((NSNumber *)equalities.lastObject)
+#define equalitiesLastValue ((NSNumber *)equalities.lastObject).integerValue
+
+ if (diffs == nil || diffs.count == 0) {
+ return;
+ }
+
+ BOOL changes = NO;
+ // Stack of indices where equalities are found.
+ NSMutableArray *equalities = [NSMutableArray array];
+ // Always equal to equalities.lastObject.text
+ NSString *lastEquality = nil;
+ NSUInteger thisPointer = 0; // Index of current position.
+ // Number of characters that changed prior to the equality.
+ NSUInteger length_insertions1 = 0;
+ NSUInteger length_deletions1 = 0;
+ // Number of characters that changed after the equality.
+ NSUInteger length_insertions2 = 0;
+ NSUInteger length_deletions2 = 0;
+
+ NSUInteger indexToChange;
+ Diff *diffToChange;
+
+ while (thisPointer < diffs.count) {
+ if (thisDiff.operation == DIFF_EQUAL) { // Equality found.
+ [equalities addObject:[NSNumber numberWithInteger:thisPointer]];
+ length_insertions1 = length_insertions2;
+ length_deletions1 = length_deletions2;
+ length_insertions2 = 0;
+ length_deletions2 = 0;
+ lastEquality = thisDiff.text;
+ } else { // an insertion or deletion
+ if (thisDiff.operation == DIFF_INSERT) {
+ length_insertions2 += thisDiff.text.length;
+ } else {
+ length_deletions2 += thisDiff.text.length;
+ }
+ // Eliminate an equality that is smaller or equal to the edits on both
+ // sides of it.
+ if (lastEquality != nil
+ && (lastEquality.length <= MAX(length_insertions1, length_deletions1))
+ && (lastEquality.length <= MAX(length_insertions2, length_deletions2))) {
+ // Duplicate record.
+ [diffs insertObject:[Diff diffWithOperation:DIFF_DELETE andText:lastEquality] atIndex:equalitiesLastValue];
+ // Change second copy to insert.
+ // Hash values for objects must not change while in a collection.
+ indexToChange = equalitiesLastValue + 1;
+ diffToChange = [[diffs objectAtIndex:indexToChange] retain];
+ [diffs replaceObjectAtIndex:indexToChange withObject:[NSNull null]];
+ diffToChange.operation = DIFF_INSERT;
+ [diffs replaceObjectAtIndex:indexToChange withObject:diffToChange];
+ [diffToChange release];
+
+ // Throw away the equality we just deleted.
+ [equalities removeLastObject];
+ if (equalities.count > 0) {
+ [equalities removeLastObject];
+ }
+ // Setting an unsigned value to -1 may seem weird to some,
+ // but we will pass thru a ++ below:
+ // => overflow => 0
+ thisPointer = equalities.count > 0 ? equalitiesLastValue : -1;
+ length_insertions1 = 0; // Reset the counters.
+ length_deletions1 = 0;
+ length_insertions2 = 0;
+ length_deletions2 = 0;
+ lastEquality = nil;
+ changes = YES;
+ }
+ }
+ thisPointer++;
+ }
+
+ // Normalize the diff.
+ if (changes) {
+ [self diff_cleanupMerge:diffs];
+ }
+ [self diff_cleanupSemanticLossless:diffs];
+
+ // Find any overlaps between deletions and insertions.
+ // e.g: abcxxxxxxdef
+ // -> abcxxxdef
+ // e.g: xxxabcdefxxx
+ // -> defxxxabc
+ // Only extract an overlap if it is as big as the edit ahead or behind it.
+ thisPointer = 1;
+ while (thisPointer < diffs.count) {
+ if (prevDiff.operation == DIFF_DELETE && thisDiff.operation == DIFF_INSERT) {
+ NSString *deletion = [prevDiff.text copy];
+ NSString *insertion = [thisDiff.text copy];
+ NSUInteger overlap_length1 = (NSUInteger)diff_commonOverlap((CFStringRef)deletion, (CFStringRef)insertion);
+ NSUInteger overlap_length2 = (NSUInteger)diff_commonOverlap((CFStringRef)insertion, (CFStringRef)deletion);
+ if (overlap_length1 >= overlap_length2) {
+ if (overlap_length1 >= deletion.length / 2.0 ||
+ overlap_length1 >= insertion.length / 2.0) {
+ // Overlap found.
+ // Insert an equality and trim the surrounding edits.
+ [diffs insertObject:[Diff diffWithOperation:DIFF_EQUAL
+ andText:[insertion substringWithRange:NSMakeRange(0, overlap_length1)]]
+ atIndex:thisPointer];
+ prevDiff.text = [deletion substringWithRange:NSMakeRange(0, deletion.length - overlap_length1)];
+ nextDiff.text = [insertion substringFromIndex:overlap_length1];
+ thisPointer++;
+ }
+ } else {
+ if (overlap_length2 >= deletion.length / 2.0 ||
+ overlap_length2 >= insertion.length / 2.0) {
+ // Reverse overlap found.
+ // Insert an equality and swap and trim the surrounding edits.
+ [diffs insertObject:[Diff diffWithOperation:DIFF_EQUAL
+ andText:[deletion substringWithRange:NSMakeRange(0, overlap_length2)]]
+ atIndex:thisPointer];
+ prevDiff.operation = DIFF_INSERT;
+ prevDiff.text = [insertion substringWithRange:NSMakeRange(0, insertion.length - overlap_length2)];
+ nextDiff.operation = DIFF_DELETE;
+ nextDiff.text = [deletion substringFromIndex:overlap_length2];
+ thisPointer++;
+ }
+ }
+ [deletion release];
+ [insertion release];
+ thisPointer++;
+ }
+ thisPointer++;
+ }
+
+#undef prevDiff
+#undef thisDiff
+#undef nextDiff
+#undef equalitiesLastItem
+#undef equalitiesLastValue
+}
+
+#pragma mark Match Functions
+// MATCH FUNCTIONS
+
+
+/**
+ * Locate the best instance of 'pattern' in 'text' near 'loc'.
+ * Returns NSNotFound if no match found.
+ * @param text The text to search.
+ * @param pattern The pattern to search for.
+ * @param loc The location to search around.
+ * @return Best match index or NSNotFound.
+ */
+- (NSUInteger)match_mainForText:(NSString *)text
+ pattern:(NSString *)pattern
+ near:(NSUInteger)loc;
+{
+ // Check for null inputs.
+ if (text == nil || pattern == nil) {
+ NSLog(@"Null inputs. (match_main)");
+ return NSNotFound;
+ }
+ if (text.length == 0) {
+ NSLog(@"Empty text. (match_main)");
+ return NSNotFound;
+ }
+
+ NSUInteger new_loc;
+ new_loc = MIN(loc, text.length);
+ new_loc = MAX((NSUInteger)0, new_loc);
+
+ if ([text isEqualToString:pattern]) {
+ // Shortcut (potentially not guaranteed by the algorithm)
+ return 0;
+ } else if (text.length == 0) {
+ // Nothing to match.
+ return NSNotFound;
+ } else if (new_loc + pattern.length <= text.length
+ && [[text substringWithRange:NSMakeRange(new_loc, pattern.length)] isEqualToString:pattern]) {
+ // Perfect match at the perfect spot! (Includes case of empty pattern)
+ return new_loc;
+ } else {
+ // Do a fuzzy compare.
+ return [self match_bitapOfText:text andPattern:pattern near:new_loc];
+ }
+}
+
+/**
+ * Locate the best instance of 'pattern' in 'text' near 'loc' using the
+ * Bitap algorithm. Returns NSNotFound if no match found.
+ * @param text The text to search.
+ * @param pattern The pattern to search for.
+ * @param loc The location to search around.
+ * @return Best match index or NSNotFound.
+ */
+- (NSUInteger)match_bitapOfText:(NSString *)text
+ andPattern:(NSString *)pattern
+ near:(NSUInteger)loc;
+{
+ NSAssert((Match_MaxBits == 0 || pattern.length <= Match_MaxBits),
+ @"Pattern too long for this application.");
+
+ // Initialise the alphabet.
+ NSMutableDictionary *s = [self match_alphabet:pattern];
+
+ // Highest score beyond which we give up.
+ double score_threshold = Match_Threshold;
+ // Is there a nearby exact match? (speedup)
+ NSUInteger best_loc = [text rangeOfString:pattern options:NSLiteralSearch range:NSMakeRange(loc, text.length - loc)].location;
+ if (best_loc != NSNotFound) {
+ score_threshold = MIN([self match_bitapScoreForErrorCount:0 location:best_loc near:loc pattern:pattern], score_threshold);
+ // What about in the other direction? (speedup)
+ NSUInteger searchRangeLoc = MIN(loc + pattern.length, text.length);
+ NSRange searchRange = NSMakeRange(0, searchRangeLoc);
+ best_loc = [text rangeOfString:pattern options:(NSLiteralSearch | NSBackwardsSearch) range:searchRange].location;
+ if (best_loc != NSNotFound) {
+ score_threshold = MIN([self match_bitapScoreForErrorCount:0 location:best_loc near:loc pattern:pattern], score_threshold);
+ }
+ }
+
+ // Initialise the bit arrays.
+ NSUInteger matchmask = 1 << (pattern.length - 1);
+ best_loc = NSNotFound;
+
+ NSUInteger bin_min, bin_mid;
+ NSUInteger bin_max = pattern.length + text.length;
+ NSUInteger *rd = NULL;
+ NSUInteger *last_rd = NULL;
+ for (NSUInteger d = 0; d < pattern.length; d++) {
+ // Scan for the best match; each iteration allows for one more error.
+ // Run a binary search to determine how far from 'loc' we can stray at
+ // this error level.
+ bin_min = 0;
+ bin_mid = bin_max;
+ while (bin_min < bin_mid) {
+ double score = [self match_bitapScoreForErrorCount:d location:(loc + bin_mid) near:loc pattern:pattern];
+ if (score <= score_threshold) {
+ bin_min = bin_mid;
+ } else {
+ bin_max = bin_mid;
+ }
+ bin_mid = (bin_max - bin_min) / 2 + bin_min;
+ }
+ // Use the result from this iteration as the maximum for the next.
+ bin_max = bin_mid;
+ NSUInteger start = MAX_OF_CONST_AND_DIFF(1, loc, bin_mid);
+ NSUInteger finish = MIN(loc + bin_mid, text.length) + pattern.length;
+
+ rd = (NSUInteger *)calloc((finish + 2), sizeof(NSUInteger));
+ rd[finish + 1] = (1 << d) - 1;
+
+ for (NSUInteger j = finish; j >= start; j--) {
+ NSUInteger charMatch;
+ if (text.length <= j - 1 || ![s diff_containsObjectForUnicharKey:[text characterAtIndex:(j - 1)]]) {
+ // Out of range.
+ charMatch = 0;
+ } else {
+ charMatch = [s diff_unsignedIntegerForUnicharKey:[text characterAtIndex:(j - 1)]];
+ }
+ if (d == 0) {
+ // First pass: exact match.
+ rd[j] = ((rd[j + 1] << 1) | 1) & charMatch;
+ } else {
+ // Subsequent passes: fuzzy match.
+ rd[j] = (((rd[j + 1] << 1) | 1) & charMatch)
+ | (((last_rd[j + 1] | last_rd[j]) << 1) | 1) | last_rd[j + 1];
+ }
+ if ((rd[j] & matchmask) != 0) {
+ double score = [self match_bitapScoreForErrorCount:d location:(j - 1) near:loc pattern:pattern];
+ // This match will almost certainly be better than any existing match.
+ // But check anyway.
+ if (score <= score_threshold) {
+ // Told you so.
+ score_threshold = score;
+ best_loc = j - 1;
+ if (best_loc > loc) {
+ // When passing loc, don't exceed our current distance from loc.
+ start = MAX_OF_CONST_AND_DIFF(1, 2 * loc, best_loc);
+ } else {
+ // Already passed loc, downhill from here on in.
+ break;
+ }
+ }
+ }
+ }
+ if ([self match_bitapScoreForErrorCount:(d + 1) location:loc near:loc pattern:pattern] > score_threshold) {
+ // No hope for a (better) match at greater error levels.
+ break;
+ }
+
+ if (last_rd != NULL) {
+ free(last_rd);
+ }
+ last_rd = rd;
+ }
+
+ if (rd != NULL && last_rd != rd) {
+ free(rd);
+ }
+ if (last_rd != NULL) {
+ free(last_rd);
+ }
+
+ return best_loc;
+}
+
+/**
+ * Compute and return the score for a match with e errors and x location.
+ * @param e Number of errors in match.
+ * @param x Location of match.
+ * @param loc Expected location of match.
+ * @param pattern Pattern being sought.
+ * @return Overall score for match (0.0 = good, 1.0 = bad).
+ */
+- (double)match_bitapScoreForErrorCount:(NSUInteger)e
+ location:(NSUInteger)x
+ near:(NSUInteger)loc
+ pattern:(NSString *)pattern;
+{
+ double score;
+
+ double accuracy = (double)e / pattern.length;
+ NSUInteger proximity = (NSUInteger)ABS((long long)loc - (long long)x);
+ if (Match_Distance == 0) {
+ // Dodge divide by zero error.
+ return proximity == 0 ? accuracy : 1.0;
+ }
+ score = accuracy + (proximity / (double) Match_Distance);
+
+ return score;
+}
+
+/**
+ * Initialise the alphabet for the Bitap algorithm.
+ * @param pattern The text to encode.
+ * @return Hash of character locations
+ * (NSMutableDictionary: keys:NSString/unichar, values:NSNumber/NSUInteger).
+ */
+- (NSMutableDictionary *)match_alphabet:(NSString *)pattern;
+{
+ NSMutableDictionary *s = [NSMutableDictionary dictionary];
+ CFStringRef str = (CFStringRef)pattern;
+ CFStringInlineBuffer inlineBuffer;
+ CFIndex length;
+ CFIndex cnt;
+
+ length = CFStringGetLength(str);
+ CFStringInitInlineBuffer(str, &inlineBuffer, CFRangeMake(0, length));
+
+ UniChar ch;
+ CFStringRef c;
+ for (cnt = 0; cnt < length; cnt++) {
+ ch = CFStringGetCharacterFromInlineBuffer(&inlineBuffer, cnt);
+ c = diff_CFStringCreateFromUnichar(ch);
+ if (![s diff_containsObjectForKey:(NSString *)c]) {
+ [s diff_setUnsignedIntegerValue:0 forKey:(NSString *)c];
+ }
+ CFRelease(c);
+ }
+
+ NSUInteger i = 0;
+ for (cnt = 0; cnt < length; cnt++) {
+ ch = CFStringGetCharacterFromInlineBuffer(&inlineBuffer, cnt);
+ c = diff_CFStringCreateFromUnichar(ch);
+ NSUInteger value = [s diff_unsignedIntegerForKey:(NSString *)c] | (1 << (pattern.length - i - 1));
+ [s diff_setUnsignedIntegerValue:value forKey:(NSString *)c];
+ i++;
+ CFRelease(c);
+ }
+ return s;
+}
+
+
+#pragma mark Patch Functions
+// PATCH FUNCTIONS
+
+
+/**
+ * Increase the context until it is unique,
+ * but don't let the pattern expand beyond Match_MaxBits.
+ * @param patch The patch to grow.
+ * @param text Source text.
+ */
+- (void)patch_addContextToPatch:(Patch *)patch
+ sourceText:(NSString *)text;
+{
+ if (text.length == 0) {
+ return;
+ }
+ NSString *pattern = [text substringWithRange:NSMakeRange(patch.start2, patch.length1)];
+ NSUInteger padding = 0;
+
+ // Look for the first and last matches of pattern in text. If two
+ // different matches are found, increase the pattern length.
+ while ([text rangeOfString:pattern options:NSLiteralSearch].location
+ != [text rangeOfString:pattern options:(NSLiteralSearch | NSBackwardsSearch)].location
+ && pattern.length < (Match_MaxBits - Patch_Margin - Patch_Margin)) {
+ padding += Patch_Margin;
+ pattern = [text diff_javaSubstringFromStart:MAX_OF_CONST_AND_DIFF(0, patch.start2, padding)
+ toEnd:MIN(text.length, patch.start2 + patch.length1 + padding)];
+ }
+ // Add one chunk for good luck.
+ padding += Patch_Margin;
+
+ // Add the prefix.
+ NSString *prefix = [text diff_javaSubstringFromStart:MAX_OF_CONST_AND_DIFF(0, patch.start2, padding)
+ toEnd:patch.start2];
+ if (prefix.length != 0) {
+ [patch.diffs insertObject:[Diff diffWithOperation:DIFF_EQUAL andText:prefix] atIndex:0];
+ }
+ // Add the suffix.
+ NSString *suffix = [text diff_javaSubstringFromStart:(patch.start2 + patch.length1)
+ toEnd:MIN(text.length, patch.start2 + patch.length1 + padding)];
+ if (suffix.length != 0) {
+ [patch.diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:suffix]];
+ }
+
+ // Roll back the start points.
+ patch.start1 -= prefix.length;
+ patch.start2 -= prefix.length;
+ // Extend the lengths.
+ patch.length1 += prefix.length + suffix.length;
+ patch.length2 += prefix.length + suffix.length;
+}
+
+/**
+ * Compute a list of patches to turn text1 into text2.
+ * A set of diffs will be computed.
+ * @param text1 Old text.
+ * @param text2 New text.
+ * @return NSMutableArray of Patch objects.
+ */
+- (NSMutableArray *)patch_makeFromOldString:(NSString *)text1
+ andNewString:(NSString *)text2;
+{
+ // Check for null inputs.
+ if (text1 == nil || text2 == nil) {
+ NSLog(@"Null inputs. (patch_make)");
+ return nil;
+ }
+
+ // No diffs provided, compute our own.
+ NSMutableArray *diffs = [self diff_mainOfOldString:text1 andNewString:text2 checkLines:YES];
+ if (diffs.count > 2) {
+ [self diff_cleanupSemantic:diffs];
+ [self diff_cleanupEfficiency:diffs];
+ }
+
+ return [self patch_makeFromOldString:text1 andDiffs:diffs];
+}
+
+/**
+ * Compute a list of patches to turn text1 into text2.
+ * text1 will be derived from the provided diffs.
+ * @param diffs NSMutableArray of Diff objects for text1 to text2.
+ * @return NSMutableArray of Patch objects.
+ */
+- (NSMutableArray *)patch_makeFromDiffs:(NSMutableArray *)diffs;
+{
+ // Check for nil inputs not needed since nil can't be passed in C#.
+ // No origin NSString *provided, comAdde our own.
+ NSString *text1 = [self diff_text1:diffs];
+ return [self patch_makeFromOldString:text1 andDiffs:diffs];
+}
+
+/**
+ * Compute a list of patches to turn text1 into text2.
+ * text2 is ignored, diffs are the delta between text1 and text2.
+ * @param text1 Old text
+ * @param text2 New text
+ * @param diffs NSMutableArray of Diff objects for text1 to text2.
+ * @return NSMutableArray of Patch objects.
+ * @deprecated Prefer -patch_makeFromOldString:diffs:.
+ */
+- (NSMutableArray *)patch_makeFromOldString:(NSString *)text1
+ newString:(NSString *)text2
+ diffs:(NSMutableArray *)diffs __deprecated;
+{
+ // Check for null inputs.
+ if (text1 == nil || text2 == nil) {
+ NSLog(@"Null inputs. (patch_make)");
+ return nil;
+ }
+
+ return [self patch_makeFromOldString:text1 andDiffs:diffs];
+}
+
+/**
+ * Compute a list of patches to turn text1 into text2.
+ * text2 is not provided, diffs are the delta between text1 and text2.
+ * @param text1 Old text.
+ * @param diffs NSMutableArray of Diff objects for text1 to text2.
+ * @return NSMutableArray of Patch objects.
+ */
+- (NSMutableArray *)patch_makeFromOldString:(NSString *)text1
+ andDiffs:(NSMutableArray *)diffs;
+{
+ // Check for null inputs.
+ if (text1 == nil) {
+ NSLog(@"Null inputs. (patch_make)");
+ return nil;
+ }
+
+ NSMutableArray *patches = [NSMutableArray array];
+ if (diffs.count == 0) {
+ return patches; // Get rid of the nil case.
+ }
+ Patch *patch = [[Patch new] autorelease];
+ NSUInteger char_count1 = 0; // Number of characters into the text1 NSString.
+ NSUInteger char_count2 = 0; // Number of characters into the text2 NSString.
+ // Start with text1 (prepatch_text) and apply the diffs until we arrive at
+ // text2 (postpatch_text). We recreate the patches one by one to determine
+ // context info.
+ NSString *prepatch_text = [text1 retain];
+ NSMutableString *postpatch_text = [text1 mutableCopy];
+ for (Diff *aDiff in diffs) {
+ if (patch.diffs.count == 0 && aDiff.operation != DIFF_EQUAL) {
+ // A new patch starts here.
+ patch.start1 = char_count1;
+ patch.start2 = char_count2;
+ }
+
+ switch (aDiff.operation) {
+ case DIFF_INSERT:
+ [patch.diffs addObject:aDiff];
+ patch.length2 += aDiff.text.length;
+ [postpatch_text insertString:aDiff.text atIndex:char_count2];
+ break;
+ case DIFF_DELETE:
+ patch.length1 += aDiff.text.length;
+ [patch.diffs addObject:aDiff];
+ [postpatch_text deleteCharactersInRange:NSMakeRange(char_count2, aDiff.text.length)];
+ break;
+ case DIFF_EQUAL:
+ if (aDiff.text.length <= 2 * Patch_Margin
+ && [patch.diffs count] != 0 && aDiff != diffs.lastObject) {
+ // Small equality inside a patch.
+ [patch.diffs addObject:aDiff];
+ patch.length1 += aDiff.text.length;
+ patch.length2 += aDiff.text.length;
+ }
+
+ if (aDiff.text.length >= 2 * Patch_Margin) {
+ // Time for a new patch.
+ if (patch.diffs.count != 0) {
+ [self patch_addContextToPatch:patch sourceText:prepatch_text];
+ [patches addObject:patch];
+ patch = [[Patch new] autorelease];
+ // Unlike Unidiff, our patch lists have a rolling context.
+ // https://github.com/google/diff-match-patch/wiki/Unidiff
+ // Update prepatch text & pos to reflect the application of the
+ // just completed patch.
+ [prepatch_text release];
+ prepatch_text = [postpatch_text copy];
+ char_count1 = char_count2;
+ }
+ }
+ break;
+ }
+
+ // Update the current character count.
+ if (aDiff.operation != DIFF_INSERT) {
+ char_count1 += aDiff.text.length;
+ }
+ if (aDiff.operation != DIFF_DELETE) {
+ char_count2 += aDiff.text.length;
+ }
+ }
+ // Pick up the leftover patch if not empty.
+ if (patch.diffs.count != 0) {
+ [self patch_addContextToPatch:patch sourceText:prepatch_text];
+ [patches addObject:patch];
+ }
+
+ [prepatch_text release];
+ [postpatch_text release];
+
+ return patches;
+}
+
+/**
+ * Given an array of patches, return another array that is identical.
+ * @param patches NSArray of Patch objects.
+ * @return NSMutableArray of Patch objects.
+ */
+- (NSMutableArray *)patch_deepCopy:(NSArray *)patches;
+{
+ NSMutableArray *patchesCopy = [[NSMutableArray alloc] initWithArray:patches copyItems:YES];
+ return patchesCopy;
+}
+
+/**
+ * Merge a set of patches onto the text. Return a patched text, as well
+ * as an array of YES/NO values indicating which patches were applied.
+ * @param sourcePatches NSMutableArray of Patch objects
+ * @param text Old text.
+ * @return Two element NSArray, containing the new text and an array of
+ * BOOL values.
+ */
+- (NSArray *)patch_apply:(NSArray *)sourcePatches
+ toString:(NSString *)text;
+{
+ if (sourcePatches.count == 0) {
+ return [NSArray arrayWithObjects:text, [NSMutableArray array], nil];
+ }
+
+ // Deep copy the patches so that no changes are made to originals.
+ NSMutableArray *patches = [self patch_deepCopy:sourcePatches];
+
+ NSMutableString *textMutable = [[text mutableCopy] autorelease];
+
+ NSString *nullPadding = [self patch_addPadding:patches];
+ [textMutable insertString:nullPadding atIndex:0];
+ [textMutable appendString:nullPadding];
+ [self patch_splitMax:patches];
+
+ NSUInteger x = 0;
+ // delta keeps track of the offset between the expected and actual
+ // location of the previous patch. If there are patches expected at
+ // positions 10 and 20, but the first patch was found at 12, delta is 2
+ // and the second patch has an effective expected position of 22.
+ NSUInteger delta = 0;
+ BOOL *results = (BOOL *)calloc(patches.count, sizeof(BOOL));
+ for (Patch *aPatch in patches) {
+ NSUInteger expected_loc = aPatch.start2 + delta;
+ NSString *text1 = [self diff_text1:aPatch.diffs];
+ NSUInteger start_loc;
+ NSUInteger end_loc = NSNotFound;
+ if (text1.length > Match_MaxBits) {
+ // patch_splitMax will only provide an oversized pattern
+ // in the case of a monster delete.
+ start_loc = [self match_mainForText:textMutable
+ pattern:[text1 substringWithRange:NSMakeRange(0, Match_MaxBits)]
+ near:expected_loc];
+ if (start_loc != NSNotFound) {
+ end_loc = [self match_mainForText:textMutable
+ pattern:[text1 substringFromIndex:text1.length - Match_MaxBits]
+ near:(expected_loc + text1.length - Match_MaxBits)];
+ if (end_loc == NSNotFound || start_loc >= end_loc) {
+ // Can't find valid trailing context. Drop this patch.
+ start_loc = NSNotFound;
+ }
+ }
+ } else {
+ start_loc = [self match_mainForText:textMutable pattern:text1 near:expected_loc];
+ }
+ if (start_loc == NSNotFound) {
+ // No match found. :(
+ results[x] = NO;
+ // Subtract the delta for this failed patch from subsequent patches.
+ delta -= aPatch.length2 - aPatch.length1;
+ } else {
+ // Found a match. :)
+ results[x] = YES;
+ delta = start_loc - expected_loc;
+ NSString *text2;
+ if (end_loc == NSNotFound) {
+ text2 = [textMutable diff_javaSubstringFromStart:start_loc
+ toEnd:MIN(start_loc + text1.length, textMutable.length)];
+ } else {
+ text2 = [textMutable diff_javaSubstringFromStart:start_loc
+ toEnd:MIN(end_loc + Match_MaxBits, textMutable.length)];
+ }
+ if (text1 == text2) {
+ // Perfect match, just shove the Replacement text in.
+ [textMutable replaceCharactersInRange:NSMakeRange(start_loc, text1.length) withString:[self diff_text2:aPatch.diffs]];
+ } else {
+ // Imperfect match. Run a diff to get a framework of equivalent
+ // indices.
+ NSMutableArray *diffs = [self diff_mainOfOldString:text1 andNewString:text2 checkLines:NO];
+ if (text1.length > Match_MaxBits
+ && ([self diff_levenshtein:diffs] / (float)text1.length)
+ > Patch_DeleteThreshold) {
+ // The end points match, but the content is unacceptably bad.
+ results[x] = NO;
+ } else {
+ [self diff_cleanupSemanticLossless:diffs];
+ NSUInteger index1 = 0;
+ for (Diff *aDiff in aPatch.diffs) {
+ if (aDiff.operation != DIFF_EQUAL) {
+ NSUInteger index2 = [self diff_xIndexIn:diffs location:index1];
+ if (aDiff.operation == DIFF_INSERT) {
+ // Insertion
+ [textMutable insertString:aDiff.text atIndex:(start_loc + index2)];
+ } else if (aDiff.operation == DIFF_DELETE) {
+ // Deletion
+ [textMutable deleteCharactersInRange:NSMakeRange(start_loc + index2,
+ ([self diff_xIndexIn:diffs
+ location:(index1 + aDiff.text.length)] - index2))];
+ }
+ }
+ if (aDiff.operation != DIFF_DELETE) {
+ index1 += aDiff.text.length;
+ }
+ }
+ }
+ }
+ }
+ x++;
+ }
+
+ NSMutableArray *resultsArray = [NSMutableArray arrayWithCapacity:patches.count];
+ for (NSUInteger i = 0; i < patches.count; i++) {
+ [resultsArray addObject:[NSNumber numberWithBool:(results[i])]];
+ }
+
+ if (results != NULL) {
+ free(results);
+ }
+
+ // Strip the padding off.
+ text = [textMutable substringWithRange:NSMakeRange(nullPadding.length,
+ textMutable.length - 2 * nullPadding.length)];
+ [patches release];
+ return [NSArray arrayWithObjects:text, resultsArray, nil];
+}
+
+/**
+ * Add some padding on text start and end so that edges can match something.
+ * Intended to be called only from within patch_apply.
+ * @param patches NSMutableArray of Patch objects.
+ * @return The padding NSString added to each side.
+ */
+- (NSString *)patch_addPadding:(NSMutableArray *)patches;
+{
+ uint16_t paddingLength = Patch_Margin;
+ NSMutableString *nullPadding = [NSMutableString string];
+ for (UniChar x = 1; x <= paddingLength; x++) {
+ CFStringAppendCharacters((CFMutableStringRef)nullPadding, &x, 1);
+ }
+
+ // Bump all the patches forward.
+ for (Patch *aPatch in patches) {
+ aPatch.start1 += paddingLength;
+ aPatch.start2 += paddingLength;
+ }
+
+ // Add some padding on start of first diff.
+ Patch *patch = [patches objectAtIndex:0];
+ NSMutableArray *diffs = patch.diffs;
+ if (diffs.count == 0 || ((Diff *)[diffs objectAtIndex:0]).operation != DIFF_EQUAL) {
+ // Add nullPadding equality.
+ [diffs insertObject:[Diff diffWithOperation:DIFF_EQUAL andText:nullPadding] atIndex:0];
+ patch.start1 -= paddingLength; // Should be 0.
+ patch.start2 -= paddingLength; // Should be 0.
+ patch.length1 += paddingLength;
+ patch.length2 += paddingLength;
+ } else if (paddingLength > ((Diff *)[diffs objectAtIndex:0]).text.length) {
+ // Grow first equality.
+ Diff *firstDiff = [diffs objectAtIndex:0];
+ NSUInteger extraLength = paddingLength - firstDiff.text.length;
+ firstDiff.text = [[nullPadding substringFromIndex:(firstDiff.text.length)]
+ stringByAppendingString:firstDiff.text];
+ patch.start1 -= extraLength;
+ patch.start2 -= extraLength;
+ patch.length1 += extraLength;
+ patch.length2 += extraLength;
+ }
+
+ // Add some padding on end of last diff.
+ patch = patches.lastObject;
+ diffs = patch.diffs;
+ if (diffs.count == 0 || ((Diff *)diffs.lastObject).operation != DIFF_EQUAL) {
+ // Add nullPadding equality.
+ [diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:nullPadding]];
+ patch.length1 += paddingLength;
+ patch.length2 += paddingLength;
+ } else if (paddingLength > ((Diff *)diffs.lastObject).text.length) {
+ // Grow last equality.
+ Diff *lastDiff = diffs.lastObject;
+ NSUInteger extraLength = paddingLength - lastDiff.text.length;
+ lastDiff.text = [lastDiff.text stringByAppendingString:[nullPadding substringWithRange:NSMakeRange(0, extraLength)]];
+ patch.length1 += extraLength;
+ patch.length2 += extraLength;
+ }
+
+ return nullPadding;
+}
+
+/**
+ * Look through the patches and break up any which are longer than the
+ * maximum limit of the match algorithm.
+ * Intended to be called only from within patch_apply.
+ * @param patches NSMutableArray of Patch objects.
+ */
+- (void)patch_splitMax:(NSMutableArray *)patches;
+{
+ NSUInteger patch_size = Match_MaxBits;
+ for (NSUInteger x = 0; x < patches.count; x++) {
+ if (((Patch *)[patches objectAtIndex:x]).length1 <= patch_size) {
+ continue;
+ }
+ Patch *bigpatch = [[patches objectAtIndex:x] retain];
+ // Remove the big old patch.
+ splice(patches, x--, 1, nil);
+ NSUInteger start1 = bigpatch.start1;
+ NSUInteger start2 = bigpatch.start2;
+ NSString *precontext = @"";
+ while (bigpatch.diffs.count != 0) {
+ // Create one of several smaller patches.
+ Patch *patch = [[Patch new] autorelease];
+ BOOL empty = YES;
+ patch.start1 = start1 - precontext.length;
+ patch.start2 = start2 - precontext.length;
+ if (precontext.length != 0) {
+ patch.length1 = patch.length2 = precontext.length;
+ [patch.diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:precontext]];
+ }
+ while (bigpatch.diffs.count != 0
+ && patch.length1 < patch_size - self.Patch_Margin) {
+ Operation diff_type = ((Diff *)[bigpatch.diffs objectAtIndex:0]).operation;
+ NSString *diff_text = ((Diff *)[bigpatch.diffs objectAtIndex:0]).text;
+ if (diff_type == DIFF_INSERT) {
+ // Insertions are harmless.
+ patch.length2 += diff_text.length;
+ start2 += diff_text.length;
+ [patch.diffs addObject:[bigpatch.diffs objectAtIndex:0]];
+ [bigpatch.diffs removeObjectAtIndex:0];
+ empty = NO;
+ } else if (diff_type == DIFF_DELETE && patch.diffs.count == 1
+ && ((Diff *)[patch.diffs objectAtIndex:0]).operation == DIFF_EQUAL
+ && diff_text.length > 2 * patch_size) {
+ // This is a large deletion. Let it pass in one chunk.
+ patch.length1 += diff_text.length;
+ start1 += diff_text.length;
+ empty = NO;
+ [patch.diffs addObject:[Diff diffWithOperation:diff_type andText:diff_text]];
+ [bigpatch.diffs removeObjectAtIndex:0];
+ } else {
+ // Deletion or equality. Only take as much as we can stomach.
+ diff_text = [diff_text substringWithRange:NSMakeRange(0,
+ MIN(diff_text.length,
+ (patch_size - patch.length1 - Patch_Margin)))];
+ patch.length1 += diff_text.length;
+ start1 += diff_text.length;
+ if (diff_type == DIFF_EQUAL) {
+ patch.length2 += diff_text.length;
+ start2 += diff_text.length;
+ } else {
+ empty = NO;
+ }
+ [patch.diffs addObject:[Diff diffWithOperation:diff_type andText:diff_text]];
+ if (diff_text == ((Diff *)[bigpatch.diffs objectAtIndex:0]).text) {
+ [bigpatch.diffs removeObjectAtIndex:0];
+ } else {
+ Diff *firstDiff = [bigpatch.diffs objectAtIndex:0];
+ firstDiff.text = [firstDiff.text substringFromIndex:diff_text.length];
+ }
+ }
+ }
+ // Compute the head context for the next patch.
+ precontext = [self diff_text2:patch.diffs];
+ precontext = [precontext substringFromIndex:MAX_OF_CONST_AND_DIFF(0, precontext.length, Patch_Margin)];
+
+ NSString *postcontext = nil;
+ // Append the end context for this patch.
+ if ([self diff_text1:bigpatch.diffs].length > Patch_Margin) {
+ postcontext = [[self diff_text1:bigpatch.diffs]
+ substringWithRange:NSMakeRange(0, Patch_Margin)];
+ } else {
+ postcontext = [self diff_text1:bigpatch.diffs];
+ }
+
+ if (postcontext.length != 0) {
+ patch.length1 += postcontext.length;
+ patch.length2 += postcontext.length;
+ if (patch.diffs.count != 0
+ && ((Diff *)[patch.diffs objectAtIndex:(patch.diffs.count - 1)]).operation
+ == DIFF_EQUAL) {
+ Diff *lastDiff = [patch.diffs lastObject];
+ lastDiff.text = [lastDiff.text stringByAppendingString:postcontext];
+ } else {
+ [patch.diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:postcontext]];
+ }
+ }
+ if (!empty) {
+ splice(patches, ++x, 0, [NSMutableArray arrayWithObject:patch]);
+ }
+ }
+
+ [bigpatch release];
+
+ }
+}
+
+/**
+ * Take a list of patches and return a textual representation.
+ * @param patches NSMutableArray of Patch objects.
+ * @return Text representation of patches.
+ */
+- (NSString *)patch_toText:(NSMutableArray *)patches;
+{
+ NSMutableString *text = [NSMutableString string];
+ for (Patch *aPatch in patches) {
+ [text appendString:[aPatch description]];
+ }
+ return text;
+}
+
+/**
+ * Parse a textual representation of patches and return a NSMutableArray of
+ * Patch objects.
+ * @param textline Text representation of patches.
+ * @param error NSError if invalid input.
+ * @return NSMutableArray of Patch objects.
+ */
+- (NSMutableArray *)patch_fromText:(NSString *)textline
+ error:(NSError **)error;
+{
+ NSMutableArray *patches = [NSMutableArray array];
+ if (textline.length == 0) {
+ return patches;
+ }
+ NSArray *text = [textline componentsSeparatedByString:@"\n"];
+ NSUInteger textPointer = 0;
+ Patch *patch;
+ //NSString *patchHeader = @"^@@ -(\\d+),?(\\d*) \\+(\\d+),?(\\d*) @@$";
+ NSString *patchHeaderStart = @"@@ -";
+ NSString *patchHeaderMid = @"+";
+ NSString *patchHeaderEnd = @"@@";
+ NSString *optionalValueDelimiter = @",";
+ BOOL scanSuccess, hasOptional;
+ NSInteger scannedValue, optionalValue;
+ NSDictionary *errorDetail = nil;
+
+ unichar sign;
+ NSString *line;
+ while (textPointer < text.count) {
+ NSString *thisLine = [text objectAtIndex:textPointer];
+ NSScanner *theScanner = [NSScanner scannerWithString:thisLine];
+ patch = [[Patch new] autorelease];
+
+ scanSuccess = ([theScanner scanString:patchHeaderStart intoString:NULL]
+ && [theScanner scanInteger:&scannedValue]);
+
+ if (scanSuccess) {
+ patch.start1 = scannedValue;
+
+ hasOptional = [theScanner scanString:optionalValueDelimiter intoString:NULL];
+
+ if (hasOptional) {
+ // First set has an optional value.
+ scanSuccess = [theScanner scanInteger:&optionalValue];
+ if (scanSuccess) {
+ if (optionalValue == 0) {
+ patch.length1 = 0;
+ } else {
+ patch.start1--;
+ patch.length1 = optionalValue;
+ }
+ }
+ } else {
+ patch.start1--;
+ patch.length1 = 1;
+ }
+
+ if (scanSuccess) {
+ scanSuccess = ([theScanner scanString:patchHeaderMid intoString:NULL]
+ && [theScanner scanInteger:&scannedValue]);
+
+ if (scanSuccess) {
+ patch.start2 = scannedValue;
+
+ hasOptional = [theScanner scanString:optionalValueDelimiter intoString:NULL];
+
+ if (hasOptional) {
+ // Second set has an optional value.
+ scanSuccess = [theScanner scanInteger:&optionalValue];
+ if (scanSuccess) {
+ if (optionalValue == 0) {
+ patch.length2 = 0;
+ } else {
+ patch.start2--;
+ patch.length2 = optionalValue;
+ }
+ }
+ } else {
+ patch.start2--;
+ patch.length2 = 1;
+ }
+
+ if (scanSuccess) {
+ scanSuccess = ([theScanner scanString:patchHeaderEnd intoString:NULL]
+ && [theScanner isAtEnd] == YES);
+ }
+ }
+ }
+ }
+
+ if (!scanSuccess) {
+ if (error != NULL) {
+ errorDetail = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSString stringWithFormat:NSLocalizedString(@"Invalid patch string: %@", @"Error"),
+ [text objectAtIndex:textPointer]],
+ NSLocalizedDescriptionKey, nil];
+ *error = [NSError errorWithDomain:@"DiffMatchPatchErrorDomain" code:104 userInfo:errorDetail];
+ }
+ return nil;
+ }
+
+ [patches addObject:patch];
+
+ textPointer++;
+
+ while (textPointer < text.count) {
+ @try {
+ sign = [[text objectAtIndex:textPointer] characterAtIndex:0];
+ }
+ @catch (NSException *e) {
+ // Blank line? Whatever.
+ textPointer++;
+ continue;
+ }
+ line = [[[text objectAtIndex:textPointer] substringFromIndex:1]
+ diff_stringByReplacingPercentEscapesForEncodeUriCompatibility];
+ if (sign == '-') {
+ // Deletion.
+ [patch.diffs addObject:[Diff diffWithOperation:DIFF_DELETE andText:line]];
+ } else if (sign == '+') {
+ // Insertion.
+ [patch.diffs addObject:[Diff diffWithOperation:DIFF_INSERT andText:line]];
+ } else if (sign == ' ') {
+ // Minor equality.
+ [patch.diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:line]];
+ } else if (sign == '@') {
+ // Start of next patch.
+ break;
+ } else {
+ // WTF?
+ if (error != NULL) {
+ errorDetail = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSString stringWithFormat:NSLocalizedString(@"Invalid patch mode '%C' in: %@", @"Error"), sign, line],
+ NSLocalizedDescriptionKey, nil];
+ *error = [NSError errorWithDomain:@"DiffMatchPatchErrorDomain" code:104 userInfo:errorDetail];
+ }
+ return nil;
+ }
+ textPointer++;
+ }
+ }
+ return patches;
+}
+
+@end
diff --git a/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSMutableDictionary+DMPExtensions.h b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSMutableDictionary+DMPExtensions.h
new file mode 100755
index 0000000..6230424
--- /dev/null
+++ b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSMutableDictionary+DMPExtensions.h
@@ -0,0 +1,46 @@
+/*
+ * Diff Match and Patch
+ * Copyright 2018 The diff-match-patch Authors.
+ * https://github.com/google/diff-match-patch
+ *
+ * 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.
+ *
+ * Author: fraser@google.com (Neil Fraser)
+ * ObjC port: jan@geheimwerk.de (Jan Weiß)
+ */
+
+#import
+
+
+@interface NSMutableDictionary (DMPExtensions)
+
+- (id)diff_objectForIntegerKey:(NSInteger)keyInteger;
+- (id)diff_objectForUnsignedIntegerKey:(NSUInteger)keyUInteger;
+- (id)diff_objectForUnicharKey:(unichar)aUnicharKey;
+
+- (NSInteger)diff_integerForKey:(id)aKey;
+- (NSUInteger)diff_unsignedIntegerForKey:(id)aKey;
+- (NSInteger)diff_integerForIntegerKey:(NSInteger)keyInteger;
+- (NSUInteger)diff_unsignedIntegerForUnicharKey:(unichar)aUnicharKey;
+
+- (BOOL)diff_containsObjectForKey:(id)aKey;
+- (BOOL)diff_containsObjectForUnicharKey:(unichar)aUnicharKey;
+
+- (void)diff_setIntegerValue:(NSInteger)anInteger forKey:(id)aKey;
+- (void)diff_setIntegerValue:(NSInteger)anInteger forIntegerKey:(NSInteger)keyInteger;
+
+- (void)diff_setUnsignedIntegerValue:(NSUInteger)anUInteger forKey:(id)aKey;
+- (void)diff_setUnsignedIntegerValue:(NSUInteger)anUInteger forUnsignedIntegerKey:(NSUInteger)keyUInteger;
+- (void)diff_setUnsignedIntegerValue:(NSUInteger)anUInteger forUnicharKey:(unichar)aUnicharKey;
+
+@end
diff --git a/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSMutableDictionary+DMPExtensions.m b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSMutableDictionary+DMPExtensions.m
new file mode 100755
index 0000000..dc092be
--- /dev/null
+++ b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSMutableDictionary+DMPExtensions.m
@@ -0,0 +1,108 @@
+/*
+ * Diff Match and Patch
+ * Copyright 2018 The diff-match-patch Authors.
+ * https://github.com/google/diff-match-patch
+ *
+ * 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.
+ *
+ * Author: fraser@google.com (Neil Fraser)
+ * ObjC port: jan@geheimwerk.de (Jan Weiß)
+ */
+
+#import "NSMutableDictionary+DMPExtensions.h"
+
+#import "NSString+UnicharUtilities.h"
+
+
+@implementation NSMutableDictionary (DMPExtensions)
+
+- (id)diff_objectForIntegerKey:(NSInteger)keyInteger;
+{
+ return [self objectForKey:[NSNumber numberWithInteger:keyInteger]];
+}
+
+- (id)diff_objectForUnsignedIntegerKey:(NSUInteger)keyUInteger;
+{
+ return [self objectForKey:[NSNumber numberWithUnsignedInteger:keyUInteger]];
+}
+
+- (id)diff_objectForUnicharKey:(unichar)aUnicharKey;
+{
+ return [self objectForKey:[NSString diff_stringFromUnichar:aUnicharKey]];
+}
+
+
+- (NSInteger)diff_integerForKey:(id)aKey;
+{
+ return [((NSNumber *)[self objectForKey:aKey]) integerValue];
+}
+
+- (NSUInteger)diff_unsignedIntegerForKey:(id)aKey;
+{
+ return [((NSNumber *)[self objectForKey:aKey]) unsignedIntegerValue];
+}
+
+- (NSInteger)diff_integerForIntegerKey:(NSInteger)keyInteger;
+{
+ return [((NSNumber *)[self objectForKey:[NSNumber numberWithInteger:keyInteger]]) integerValue];
+}
+
+- (NSUInteger)diff_unsignedIntegerForUnicharKey:(unichar)aUnicharKey;
+{
+ return [((NSNumber *)[self diff_objectForUnicharKey:aUnicharKey]) unsignedIntegerValue];
+}
+
+
+- (BOOL)diff_containsObjectForKey:(id)aKey;
+{
+ return ([self objectForKey:aKey] != nil);
+}
+
+- (BOOL)containsObjectForIntegerKey:(NSInteger)keyInteger;
+{
+ return ([self objectForKey:[NSNumber numberWithInteger:keyInteger]] != nil);
+}
+
+- (BOOL)diff_containsObjectForUnicharKey:(unichar)aUnicharKey;
+{
+ return ([self diff_objectForUnicharKey:aUnicharKey] != nil);
+}
+
+
+- (void)diff_setIntegerValue:(NSInteger)anInteger forKey:(id)aKey;
+{
+ [self setObject:[NSNumber numberWithInteger:anInteger] forKey:aKey];
+}
+
+- (void)diff_setIntegerValue:(NSInteger)anInteger forIntegerKey:(NSInteger)keyInteger;
+{
+ [self setObject:[NSNumber numberWithInteger:anInteger] forKey:[NSNumber numberWithInteger:keyInteger]];
+}
+
+
+- (void)diff_setUnsignedIntegerValue:(NSUInteger)anUInteger forKey:(id)aKey;
+{
+ [self setObject:[NSNumber numberWithUnsignedInteger:anUInteger] forKey:aKey];
+}
+
+- (void)diff_setUnsignedIntegerValue:(NSUInteger)anUInteger forUnsignedIntegerKey:(NSUInteger)keyUInteger;
+{
+ [self setObject:[NSNumber numberWithUnsignedInteger:anUInteger] forKey:[NSNumber numberWithUnsignedInteger:keyUInteger]];
+}
+
+- (void)diff_setUnsignedIntegerValue:(NSUInteger)anUInteger forUnicharKey:(unichar)aUnicharKey;
+{
+ [self setObject:[NSNumber numberWithUnsignedInteger:anUInteger] forKey:[NSString diff_stringFromUnichar:aUnicharKey]];
+}
+
+@end
diff --git a/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSString+JavaSubstring.h b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSString+JavaSubstring.h
new file mode 100755
index 0000000..3f6485c
--- /dev/null
+++ b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSString+JavaSubstring.h
@@ -0,0 +1,29 @@
+/*
+ * Diff Match and Patch
+ * Copyright 2018 The diff-match-patch Authors.
+ * https://github.com/google/diff-match-patch
+ *
+ * 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.
+ *
+ * Author: fraser@google.com (Neil Fraser)
+ * ObjC port: jan@geheimwerk.de (Jan Weiß)
+ */
+
+#import
+
+
+@interface NSString (JavaSubstring)
+
+- (NSString *)diff_javaSubstringFromStart:(NSUInteger)start toEnd:(NSUInteger)end;
+
+@end
diff --git a/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSString+JavaSubstring.m b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSString+JavaSubstring.m
new file mode 100755
index 0000000..8c17dbd
--- /dev/null
+++ b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSString+JavaSubstring.m
@@ -0,0 +1,35 @@
+/*
+ * Diff Match and Patch
+ * Copyright 2018 The diff-match-patch Authors.
+ * https://github.com/google/diff-match-patch
+ *
+ * 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.
+ *
+ * Author: fraser@google.com (Neil Fraser)
+ * ObjC port: jan@geheimwerk.de (Jan Weiß)
+ */
+
+#import "NSString+JavaSubstring.h"
+
+#import "DiffMatchPatchCFUtilities.h"
+
+@implementation NSString (JavaSubstring)
+
+- (NSString *)diff_javaSubstringFromStart:(NSUInteger)start toEnd:(NSUInteger)end;
+{
+ CFStringRef c = diff_CFStringCreateJavaSubstring((CFStringRef)self, (CFIndex)start, (CFIndex)end);
+ CFMakeCollectable(c);
+ return [(NSString *)c autorelease];
+}
+
+@end
diff --git a/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSString+UnicharUtilities.h b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSString+UnicharUtilities.h
new file mode 100755
index 0000000..867196e
--- /dev/null
+++ b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSString+UnicharUtilities.h
@@ -0,0 +1,30 @@
+/*
+ * Diff Match and Patch
+ * Copyright 2018 The diff-match-patch Authors.
+ * https://github.com/google/diff-match-patch
+ *
+ * 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.
+ *
+ * Author: fraser@google.com (Neil Fraser)
+ * ObjC port: jan@geheimwerk.de (Jan Weiß)
+ */
+
+#import
+
+
+@interface NSString (UnicharUtilities)
+
++ (NSString *)diff_stringFromUnichar:(unichar)ch;
+- (NSString *)diff_substringWithCharacterAtIndex:(NSUInteger)anIndex;
+
+@end
diff --git a/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSString+UnicharUtilities.m b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSString+UnicharUtilities.m
new file mode 100755
index 0000000..a45d366
--- /dev/null
+++ b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSString+UnicharUtilities.m
@@ -0,0 +1,39 @@
+/*
+ * Diff Match and Patch
+ * Copyright 2018 The diff-match-patch Authors.
+ * https://github.com/google/diff-match-patch
+ *
+ * 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.
+ *
+ * Author: fraser@google.com (Neil Fraser)
+ * ObjC port: jan@geheimwerk.de (Jan Weiß)
+ */
+
+#import "NSString+UnicharUtilities.h"
+
+
+@implementation NSString (UnicharUtilities)
+
++ (NSString *)diff_stringFromUnichar:(unichar)ch;
+{
+ CFStringRef c = CFStringCreateWithCharacters(kCFAllocatorDefault, &ch, 1);
+ CFMakeCollectable(c);
+ return [(NSString *)c autorelease];
+}
+
+- (NSString *)diff_substringWithCharacterAtIndex:(NSUInteger)anIndex;
+{
+ return [self substringWithRange:NSMakeRange(anIndex, 1)];
+}
+
+@end
diff --git a/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSString+UriCompatibility.h b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSString+UriCompatibility.h
new file mode 100755
index 0000000..75c7873
--- /dev/null
+++ b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSString+UriCompatibility.h
@@ -0,0 +1,30 @@
+/*
+ * Diff Match and Patch
+ * Copyright 2018 The diff-match-patch Authors.
+ * https://github.com/google/diff-match-patch
+ *
+ * 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.
+ *
+ * Author: fraser@google.com (Neil Fraser)
+ * ObjC port: jan@geheimwerk.de (Jan Weiß)
+ */
+
+#import
+
+
+@interface NSString (UriCompatibility)
+
+- (NSString *)diff_stringByAddingPercentEscapesForEncodeUriCompatibility;
+- (NSString *)diff_stringByReplacingPercentEscapesForEncodeUriCompatibility;
+
+@end
diff --git a/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSString+UriCompatibility.m b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSString+UriCompatibility.m
new file mode 100755
index 0000000..67d6926
--- /dev/null
+++ b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/NSString+UriCompatibility.m
@@ -0,0 +1,55 @@
+/*
+ * Diff Match and Patch
+ * Copyright 2018 The diff-match-patch Authors.
+ * https://github.com/google/diff-match-patch
+ *
+ * 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.
+ *
+ * Author: fraser@google.com (Neil Fraser)
+ * ObjC port: jan@geheimwerk.de (Jan Weiß)
+ */
+
+#import "NSString+UriCompatibility.h"
+
+
+@implementation NSString (UriCompatibility)
+
+/**
+ * Escape excluding selected chars for compatability with JavaScript's encodeURI.
+ * This method produces uppercase hex.
+ *
+ * @return The escaped CFStringRef.
+ */
+- (NSString *)diff_stringByAddingPercentEscapesForEncodeUriCompatibility {
+ NSMutableCharacterSet *allowedCharacters =
+ [NSMutableCharacterSet characterSetWithCharactersInString:@" !~*'();/?:@&=+$,#"];
+ [allowedCharacters formUnionWithCharacterSet:[NSCharacterSet URLQueryAllowedCharacterSet]];
+ NSString *URLString =
+ [self stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacters];
+
+ return URLString;
+}
+
+/**
+ * Unescape all percent escapes.
+ *
+ * Example: "%3f" -> "?", "%24" -> "$", etc.
+ *
+ * @return The unescaped NSString.
+ */
+- (NSString *)diff_stringByReplacingPercentEscapesForEncodeUriCompatibility {
+ NSString *decodedString = [self stringByRemovingPercentEncoding];
+ return decodedString;
+}
+
+@end
diff --git a/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/other/DiffMatchPatchCFUtilities.c b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/other/DiffMatchPatchCFUtilities.c
new file mode 100755
index 0000000..b6c1064
--- /dev/null
+++ b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/other/DiffMatchPatchCFUtilities.c
@@ -0,0 +1,592 @@
+/*
+ * Diff Match and Patch
+ * Copyright 2018 The diff-match-patch Authors.
+ * https://github.com/google/diff-match-patch
+ *
+ * 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.
+ *
+ * Author: fraser@google.com (Neil Fraser)
+ * ObjC port: jan@geheimwerk.de (Jan Weiß)
+ */
+
+#include
+
+#include "DiffMatchPatchCFUtilities.h"
+
+#include "MinMaxMacros.h"
+#include
+#include
+#include
+
+CFStringRef diff_CFStringCreateSubstring(CFStringRef text, CFIndex start_index, CFIndex length);
+CFRange diff_RightSubstringRange(CFIndex text_length, CFIndex new_length);
+CFStringRef diff_CFStringCreateRightSubstring(CFStringRef text, CFIndex text_length, CFIndex new_length);
+CFRange diff_LeftSubstringRange(CFIndex new_length);
+CFStringRef diff_CFStringCreateLeftSubstring(CFStringRef text, CFIndex new_length);
+CFStringRef diff_CFStringCreateSubstringWithStartIndex(CFStringRef text, CFIndex start_index);
+CFStringRef diff_CFStringCreateJavaSubstring(CFStringRef s, CFIndex begin, CFIndex end);
+CFStringRef diff_CFStringCreateByCombiningTwoStrings(CFStringRef best_common_part1, CFStringRef best_common_part2);
+Boolean diff_regExMatch(CFStringRef text, const regex_t *re);
+
+CFArrayRef diff_halfMatchICreate(CFStringRef longtext, CFStringRef shorttext, CFIndex i);
+
+// Utility functions
+CFStringRef diff_CFStringCreateFromUnichar(UniChar ch) {
+ CFStringRef c = CFStringCreateWithCharacters(kCFAllocatorDefault, &ch, 1);
+ CFMakeCollectable(c);
+ return c;
+}
+
+CFStringRef diff_CFStringCreateSubstring(CFStringRef text, CFIndex start_index, CFIndex length) {
+ CFRange substringRange;
+ substringRange.length = length;
+ substringRange.location = start_index;
+
+ CFStringRef substring = CFStringCreateWithSubstring(kCFAllocatorDefault, text, substringRange);
+ CFMakeCollectable(substring);
+
+ return substring;
+}
+
+CFRange diff_RightSubstringRange(CFIndex text_length, CFIndex new_length) {
+ CFRange substringRange;
+ substringRange.length = new_length;
+ substringRange.location = text_length - new_length;
+ return substringRange;
+}
+
+CFStringRef diff_CFStringCreateRightSubstring(CFStringRef text, CFIndex text_length, CFIndex new_length) {
+ return diff_CFStringCreateSubstring(text, text_length - new_length, new_length);
+}
+
+CFRange diff_LeftSubstringRange(CFIndex new_length) {
+ CFRange substringRange;
+ substringRange.length = new_length;
+ substringRange.location = 0;
+ return substringRange;
+}
+
+CFStringRef diff_CFStringCreateLeftSubstring(CFStringRef text, CFIndex new_length) {
+ return diff_CFStringCreateSubstring(text, 0, new_length);
+}
+
+CFStringRef diff_CFStringCreateSubstringWithStartIndex(CFStringRef text, CFIndex start_index) {
+ return diff_CFStringCreateSubstring(text, start_index, (CFStringGetLength(text) - start_index));
+}
+
+CFStringRef diff_CFStringCreateJavaSubstring(CFStringRef s, CFIndex begin, CFIndex end) {
+ return diff_CFStringCreateSubstring(s, begin, end - begin);
+}
+
+CFStringRef diff_CFStringCreateByCombiningTwoStrings(CFStringRef best_common_part1, CFStringRef best_common_part2) {
+ CFIndex best_common_length;
+ CFMutableStringRef best_common_mutable;
+ best_common_length = CFStringGetLength(best_common_part1) + CFStringGetLength(best_common_part2);
+ best_common_mutable = CFStringCreateMutableCopy(kCFAllocatorDefault, best_common_length, best_common_part1);
+ CFMakeCollectable(best_common_mutable);
+ CFStringAppend(best_common_mutable, best_common_part2);
+ return best_common_mutable;
+}
+
+Boolean diff_regExMatch(CFStringRef text, const regex_t *re) {
+ //TODO(jan): Using regex.h is far from optimal. Find an alternative.
+ Boolean isMatch;
+ const char *bytes;
+ char *localBuffer = NULL;
+ char *textCString = NULL;
+ // We are only interested in line endings anyway so ASCII is fine.
+ CFStringEncoding encoding = kCFStringEncodingASCII;
+
+ bytes = CFStringGetCStringPtr(text, encoding);
+
+ if (bytes == NULL) {
+ Boolean success;
+ CFIndex length;
+ CFIndex usedBufferLength;
+ CFIndex textLength = CFStringGetLength(text);
+ CFRange rangeToProcess = CFRangeMake(0, textLength);
+
+ success = (CFStringGetBytes(text, rangeToProcess, encoding, '?', false, NULL, LONG_MAX, &usedBufferLength) > 0);
+ if (success) {
+ length = usedBufferLength + 1;
+
+ localBuffer = calloc(length, sizeof(char));
+ success = (CFStringGetBytes(text, rangeToProcess, encoding, '?', false, (UInt8 *)localBuffer, length, NULL) > 0);
+
+ if (success) {
+ textCString = localBuffer;
+ }
+ }
+ } else {
+ textCString = (char *)bytes;
+ }
+
+ if (textCString != NULL) {
+ isMatch = (regexec(re, textCString, 0, NULL, 0) == 0);
+ } else {
+ isMatch = false;
+ //assert(0);
+ }
+
+ if (localBuffer != NULL) {
+ free(localBuffer);
+ }
+
+ return isMatch;
+}
+
+
+/**
+ * Determine the common prefix of two strings.
+ * @param text1 First string.
+ * @param text2 Second string.
+ * @return The number of characters common to the start of each string.
+ */
+CFIndex diff_commonPrefix(CFStringRef text1, CFStringRef text2) {
+ // Performance analysis: https://neil.fraser.name/news/2007/10/09/
+ CFIndex text1_length = CFStringGetLength(text1);
+ CFIndex text2_length = CFStringGetLength(text2);
+
+ CFStringInlineBuffer text1_inlineBuffer, text2_inlineBuffer;
+ CFStringInitInlineBuffer(text1, &text1_inlineBuffer, CFRangeMake(0, text1_length));
+ CFStringInitInlineBuffer(text2, &text2_inlineBuffer, CFRangeMake(0, text2_length));
+
+ UniChar char1, char2;
+ CFIndex n = MIN(text1_length, text2_length);
+
+ for (CFIndex i = 0; i < n; i++) {
+ char1 = CFStringGetCharacterFromInlineBuffer(&text1_inlineBuffer, i);
+ char2 = CFStringGetCharacterFromInlineBuffer(&text2_inlineBuffer, i);
+
+ if (char1 != char2) {
+ return i;
+ }
+ }
+
+ return n;
+}
+
+/**
+ * Determine the common suffix of two strings.
+ * @param text1 First string.
+ * @param text2 Second string.
+ * @return The number of characters common to the end of each string.
+ */
+CFIndex diff_commonSuffix(CFStringRef text1, CFStringRef text2) {
+ // Performance analysis: https://neil.fraser.name/news/2007/10/09/
+ CFIndex text1_length = CFStringGetLength(text1);
+ CFIndex text2_length = CFStringGetLength(text2);
+
+ CFStringInlineBuffer text1_inlineBuffer, text2_inlineBuffer;
+ CFStringInitInlineBuffer(text1, &text1_inlineBuffer, CFRangeMake(0, text1_length));
+ CFStringInitInlineBuffer(text2, &text2_inlineBuffer, CFRangeMake(0, text2_length));
+
+ UniChar char1, char2;
+ CFIndex n = MIN(text1_length, text2_length);
+
+ for (CFIndex i = 1; i <= n; i++) {
+ char1 = CFStringGetCharacterFromInlineBuffer(&text1_inlineBuffer, (text1_length - i));
+ char2 = CFStringGetCharacterFromInlineBuffer(&text2_inlineBuffer, (text2_length - i));
+
+ if (char1 != char2) {
+ return i - 1;
+ }
+ }
+ return n;
+}
+
+/**
+ * Determine if the suffix of one CFStringRef is the prefix of another.
+ * @param text1 First CFStringRef.
+ * @param text2 Second CFStringRef.
+ * @return The number of characters common to the end of the first
+ * CFStringRef and the start of the second CFStringRef.
+ */
+CFIndex diff_commonOverlap(CFStringRef text1, CFStringRef text2) {
+ CFIndex common_overlap = 0;
+
+ // Cache the text lengths to prevent multiple calls.
+ CFIndex text1_length = CFStringGetLength(text1);
+ CFIndex text2_length = CFStringGetLength(text2);
+
+ // Eliminate the nil case.
+ if (text1_length == 0 || text2_length == 0) {
+ return 0;
+ }
+
+ // Truncate the longer CFStringRef.
+ CFStringRef text1_trunc;
+ CFStringRef text2_trunc;
+ CFIndex text1_trunc_length;
+ if (text1_length > text2_length) {
+ text1_trunc_length = text2_length;
+ text1_trunc = diff_CFStringCreateRightSubstring(text1, text1_length, text1_trunc_length);
+
+ text2_trunc = CFRetain(text2);
+ } else if (text1_length < text2_length) {
+ text1_trunc_length = text1_length;
+ text1_trunc = CFRetain(text1);
+
+ CFIndex text2_trunc_length = text1_length;
+ text2_trunc = diff_CFStringCreateLeftSubstring(text2, text2_trunc_length);
+ } else {
+ text1_trunc_length = text1_length;
+ text1_trunc = CFRetain(text1);
+
+ text2_trunc = CFRetain(text2);
+ }
+
+ CFIndex text_length = MIN(text1_length, text2_length);
+ // Quick check for the worst case.
+ if (text1_trunc == text2_trunc) {
+ common_overlap = text_length;
+ } else {
+ // Start by looking for a single character match
+ // and increase length until no match is found.
+ // Performance analysis: https://neil.fraser.name/news/2010/11/04/
+ CFIndex best = 0;
+ CFIndex length = 1;
+ while (true) {
+ CFStringRef pattern = diff_CFStringCreateRightSubstring(text1_trunc, text1_trunc_length, length);
+ CFRange foundRange = CFStringFind(text2_trunc, pattern, 0);
+ CFRelease(pattern);
+
+ CFIndex found = foundRange.location;
+ if (found == kCFNotFound) {
+ common_overlap = best;
+ break;
+ }
+ length += found;
+
+ CFStringRef text1_sub = diff_CFStringCreateRightSubstring(text1_trunc, text1_trunc_length, length);
+ CFStringRef text2_sub = diff_CFStringCreateLeftSubstring(text2_trunc, length);
+
+ if (found == 0 || (CFStringCompare(text1_sub, text2_sub, 0) == kCFCompareEqualTo)) {
+ best = length;
+ length++;
+ }
+
+ CFRelease(text1_sub);
+ CFRelease(text2_sub);
+ }
+ }
+
+ CFRelease(text1_trunc);
+ CFRelease(text2_trunc);
+ return common_overlap;
+}
+
+/**
+ * Do the two texts share a Substring which is at least half the length of
+ * the longer text?
+ * This speedup can produce non-minimal diffs.
+ * @param text1 First CFStringRef.
+ * @param text2 Second CFStringRef.
+ * @param diffTimeout Time limit for diff.
+ * @return Five element String array, containing the prefix of text1, the
+ * suffix of text1, the prefix of text2, the suffix of text2 and the
+ * common middle. Or NULL if there was no match.
+ */
+CFArrayRef diff_halfMatchCreate(CFStringRef text1, CFStringRef text2, const float diffTimeout) {
+ if (diffTimeout <= 0) {
+ // Don't risk returning a non-optimal diff if we have unlimited time.
+ return NULL;
+ }
+ CFStringRef longtext = CFStringGetLength(text1) > CFStringGetLength(text2) ? text1 : text2;
+ CFStringRef shorttext = CFStringGetLength(text1) > CFStringGetLength(text2) ? text2 : text1;
+ if (CFStringGetLength(longtext) < 4 || CFStringGetLength(shorttext) * 2 < CFStringGetLength(longtext)) {
+ return NULL; // Pointless.
+ }
+
+ // First check if the second quarter is the seed for a half-match.
+ CFArrayRef hm1 = diff_halfMatchICreate(longtext, shorttext,
+ (CFStringGetLength(longtext) + 3) / 4);
+ // Check again based on the third quarter.
+ CFArrayRef hm2 = diff_halfMatchICreate(longtext, shorttext,
+ (CFStringGetLength(longtext) + 1) / 2);
+ CFArrayRef hm;
+ if (hm1 == NULL && hm2 == NULL) {
+ return NULL;
+ } else if (hm2 == NULL) {
+ hm = CFRetain(hm1);
+ } else if (hm1 == NULL) {
+ hm = CFRetain(hm2);
+ } else {
+ // Both matched. Select the longest.
+ hm = CFStringGetLength(CFArrayGetValueAtIndex(hm1, 4)) > CFStringGetLength(CFArrayGetValueAtIndex(hm2, 4)) ? CFRetain(hm1) : CFRetain(hm2);
+ }
+
+ if (hm1 != NULL) {
+ CFRelease(hm1);
+ }
+ if (hm2 != NULL) {
+ CFRelease(hm2);
+ }
+
+ // A half-match was found, sort out the return data.
+ if (CFStringGetLength(text1) > CFStringGetLength(text2)) {
+ return hm;
+ //return new CFStringRef[]{hm[0], hm[1], hm[2], hm[3], hm[4]};
+ } else {
+ // { hm[0], hm[1], hm[2], hm[3], hm[4] }
+ // => { hm[2], hm[3], hm[0], hm[1], hm[4] }
+
+ CFMutableArrayRef hm_mutable = CFArrayCreateMutableCopy(kCFAllocatorDefault, CFArrayGetCount(hm), hm);
+ CFMakeCollectable(hm_mutable);
+
+ CFRelease(hm);
+
+ CFArrayExchangeValuesAtIndices(hm_mutable, 0, 2);
+ CFArrayExchangeValuesAtIndices(hm_mutable, 1, 3);
+ return hm_mutable;
+ }
+}
+
+/**
+ * Does a Substring of shorttext exist within longtext such that the
+ * Substring is at least half the length of longtext?
+ * @param longtext Longer CFStringRef.
+ * @param shorttext Shorter CFStringRef.
+ * @param i Start index of quarter length Substring within longtext.
+ * @return Five element CFStringRef array, containing the prefix of longtext, the
+ * suffix of longtext, the prefix of shorttext, the suffix of shorttext
+ * and the common middle. Or NULL if there was no match.
+ */
+CFArrayRef diff_halfMatchICreate(CFStringRef longtext, CFStringRef shorttext, CFIndex i) {
+ // Start with a 1/4 length Substring at position i as a seed.
+ CFStringRef seed = diff_CFStringCreateSubstring(longtext, i, CFStringGetLength(longtext) / 4);
+ CFIndex j = -1;
+ CFStringRef best_common = CFSTR("");
+ CFStringRef best_longtext_a = CFSTR(""), best_longtext_b = CFSTR("");
+ CFStringRef best_shorttext_a = CFSTR(""), best_shorttext_b = CFSTR("");
+
+ CFStringRef best_common_part1, best_common_part2;
+
+ CFStringRef longtext_substring, shorttext_substring;
+ CFIndex shorttext_length = CFStringGetLength(shorttext);
+ CFRange resultRange;
+ CFRange rangeToSearch;
+ rangeToSearch.length = shorttext_length - (j + 1);
+ rangeToSearch.location = j + 1;
+
+ while (j < CFStringGetLength(shorttext)
+ && (CFStringFindWithOptions(shorttext, seed, rangeToSearch, 0, &resultRange) == true)) {
+ j = resultRange.location;
+ rangeToSearch.length = shorttext_length - (j + 1);
+ rangeToSearch.location = j + 1;
+
+ longtext_substring = diff_CFStringCreateSubstringWithStartIndex(longtext, i);
+ shorttext_substring = diff_CFStringCreateSubstringWithStartIndex(shorttext, j);
+
+ CFIndex prefixLength = diff_commonPrefix(longtext_substring, shorttext_substring);
+
+ CFRelease(longtext_substring);
+ CFRelease(shorttext_substring);
+
+ longtext_substring = diff_CFStringCreateLeftSubstring(longtext, i);
+ shorttext_substring = diff_CFStringCreateLeftSubstring(shorttext, j);
+
+ CFIndex suffixLength = diff_commonSuffix(longtext_substring, shorttext_substring);
+
+ CFRelease(longtext_substring);
+ CFRelease(shorttext_substring);
+
+ if (CFStringGetLength(best_common) < suffixLength + prefixLength) {
+ CFRelease(best_common);
+ CFRelease(best_longtext_a);
+ CFRelease(best_longtext_b);
+ CFRelease(best_shorttext_a);
+ CFRelease(best_shorttext_b);
+
+ best_common_part1 = diff_CFStringCreateSubstring(shorttext, j - suffixLength, suffixLength);
+ best_common_part2 = diff_CFStringCreateSubstring(shorttext, j, prefixLength);
+
+ best_common = diff_CFStringCreateByCombiningTwoStrings(best_common_part1, best_common_part2);
+
+ CFRelease(best_common_part1);
+ CFRelease(best_common_part2);
+
+ best_longtext_a = diff_CFStringCreateLeftSubstring(longtext, i - suffixLength);
+ best_longtext_b = diff_CFStringCreateSubstringWithStartIndex(longtext, i + prefixLength);
+ best_shorttext_a = diff_CFStringCreateLeftSubstring(shorttext, j - suffixLength);
+ best_shorttext_b = diff_CFStringCreateSubstringWithStartIndex(shorttext, j + prefixLength);
+ }
+ }
+
+ CFRelease(seed);
+
+ CFArrayRef halfMatchIArray;
+ if (CFStringGetLength(best_common) * 2 >= CFStringGetLength(longtext)) {
+ const CFStringRef values[] = { best_longtext_a, best_longtext_b,
+ best_shorttext_a, best_shorttext_b, best_common };
+ halfMatchIArray = CFArrayCreate(kCFAllocatorDefault, (const void **)values, (sizeof(values) / sizeof(values[0])), &kCFTypeArrayCallBacks);
+ CFMakeCollectable(halfMatchIArray);
+ } else {
+ halfMatchIArray = NULL;
+ }
+
+ CFRelease(best_common);
+ CFRelease(best_longtext_a);
+ CFRelease(best_longtext_b);
+ CFRelease(best_shorttext_a);
+ CFRelease(best_shorttext_b);
+
+ return halfMatchIArray;
+}
+
+/**
+ * Split a text into a list of strings. Reduce the texts to a CFStringRef of
+ * hashes where each Unicode character represents one line.
+ * @param text CFString to encode.
+ * @param lineArray CFMutableArray of unique strings.
+ * @param lineHash Map of strings to indices.
+ * @param maxLines Maximum length for lineArray.
+ * @return Encoded CFStringRef.
+ */
+CFStringRef diff_linesToCharsMungeCFStringCreate(CFStringRef text, CFMutableArrayRef lineArray, CFMutableDictionaryRef lineHash, CFIndex maxLines) {
+ #define lineStart lineStartRange.location
+ #define lineEnd lineEndRange.location
+
+ CFRange lineStartRange;
+ CFRange lineEndRange;
+ lineStart = 0;
+ lineEnd = -1;
+ CFStringRef line;
+ CFMutableStringRef chars = CFStringCreateMutable(kCFAllocatorDefault, 0);
+
+ CFIndex textLength = CFStringGetLength(text);
+ CFIndex hash;
+ CFNumberRef hashNumber;
+
+ // Walk the text, pulling out a Substring for each line.
+ // CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, text, CFSTR("\n")) would temporarily double our memory footprint.
+ // Modifying text would create many large strings.
+ while (lineEnd < textLength - 1) {
+ lineStartRange.length = textLength - lineStart;
+
+ if (CFStringFindWithOptions(text, CFSTR("\n"), lineStartRange, 0, &lineEndRange) == false) {
+ lineEnd = textLength - 1;
+ } /* else {
+ lineEnd = lineEndRange.location;
+ }*/
+
+ line = diff_CFStringCreateJavaSubstring(text, lineStart, lineEnd + 1);
+
+ if (CFDictionaryContainsKey(lineHash, line)) {
+ CFDictionaryGetValueIfPresent(lineHash, line, (const void **)&hashNumber);
+ CFNumberGetValue(hashNumber, kCFNumberCFIndexType, &hash);
+ const UniChar hashChar = (UniChar)hash;
+ CFStringAppendCharacters(chars, &hashChar, 1);
+ } else {
+ if (CFArrayGetCount(lineArray) == maxLines) {
+ // Bail out at 65535 because char 65536 == char 0.
+ line = diff_CFStringCreateJavaSubstring(text, lineStart, textLength);
+ lineEnd = textLength;
+ }
+ CFArrayAppendValue(lineArray, line);
+ hash = CFArrayGetCount(lineArray) - 1;
+ hashNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &hash);
+ CFMakeCollectable(hashNumber);
+ CFDictionaryAddValue(lineHash, line, hashNumber);
+ CFRelease(hashNumber);
+ const UniChar hashChar = (UniChar)hash;
+ CFStringAppendCharacters(chars, &hashChar, 1);
+ }
+ lineStart = lineEnd + 1;
+
+ CFRelease(line);
+ }
+ return chars;
+
+ #undef lineStart
+ #undef lineEnd
+}
+
+/**
+ * Given two strings, compute a score representing whether the internal
+ * boundary falls on logical boundaries.
+ * Scores range from 6 (best) to 0 (worst).
+ * @param one First CFStringRef.
+ * @param two Second CFStringRef.
+ * @return The score.
+ */
+CFIndex diff_cleanupSemanticScore(CFStringRef one, CFStringRef two) {
+ static Boolean firstRun = true;
+ static CFCharacterSetRef alphaNumericSet = NULL;
+ static CFCharacterSetRef whiteSpaceSet = NULL;
+ static CFCharacterSetRef controlSet = NULL;
+ static regex_t blankLineEndRegEx;
+ static regex_t blankLineStartRegEx;
+
+ if (firstRun) {
+ // Define some regex patterns for matching boundaries.
+ alphaNumericSet = CFCharacterSetGetPredefined(kCFCharacterSetAlphaNumeric);
+ whiteSpaceSet = CFCharacterSetGetPredefined(kCFCharacterSetWhitespaceAndNewline);
+ controlSet = CFCharacterSetGetPredefined(kCFCharacterSetControl);
+ int status;
+ status = regcomp(&blankLineEndRegEx, "\n\r?\n$", REG_EXTENDED | REG_NOSUB);
+ assert(status == 0);
+ status = regcomp(&blankLineStartRegEx, "^\r?\n\r?\n", REG_EXTENDED | REG_NOSUB);
+ assert(status == 0);
+ firstRun = false;
+ }
+
+ if (CFStringGetLength(one) == 0 || CFStringGetLength(two) == 0) {
+ // Edges are the best.
+ return 6;
+ }
+
+ // Each port of this function behaves slightly differently due to
+ // subtle differences in each language's definition of things like
+ // 'whitespace'. Since this function's purpose is largely cosmetic,
+ // the choice has been made to use each language's native features
+ // rather than force total conformity.
+ UniChar char1 =
+ CFStringGetCharacterAtIndex(one, (CFStringGetLength(one) - 1));
+ UniChar char2 =
+ CFStringGetCharacterAtIndex(two, 0);
+ Boolean nonAlphaNumeric1 =
+ !CFCharacterSetIsCharacterMember(alphaNumericSet, char1);
+ Boolean nonAlphaNumeric2 =
+ !CFCharacterSetIsCharacterMember(alphaNumericSet, char2);
+ Boolean whitespace1 =
+ nonAlphaNumeric1 && CFCharacterSetIsCharacterMember(whiteSpaceSet, char1);
+ Boolean whitespace2 =
+ nonAlphaNumeric2 && CFCharacterSetIsCharacterMember(whiteSpaceSet, char2);
+ Boolean lineBreak1 =
+ whitespace1 && CFCharacterSetIsCharacterMember(controlSet, char1);
+ Boolean lineBreak2 =
+ whitespace2 && CFCharacterSetIsCharacterMember(controlSet, char2);
+ Boolean blankLine1 =
+ lineBreak1 && diff_regExMatch(one, &blankLineEndRegEx);
+ Boolean blankLine2 =
+ lineBreak2 && diff_regExMatch(two, &blankLineStartRegEx);
+
+ if (blankLine1 || blankLine2) {
+ // Five points for blank lines.
+ return 5;
+ } else if (lineBreak1 || lineBreak2) {
+ // Four points for line breaks.
+ return 4;
+ } else if (nonAlphaNumeric1 && !whitespace1 && whitespace2) {
+ // Three points for end of sentences.
+ return 3;
+ } else if (whitespace1 || whitespace2) {
+ // Two points for whitespace.
+ return 2;
+ } else if (nonAlphaNumeric1 || nonAlphaNumeric2) {
+ // One point for non-alphanumeric.
+ return 1;
+ }
+ return 0;
+}
diff --git a/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/other/DiffMatchPatchCFUtilities.h b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/other/DiffMatchPatchCFUtilities.h
new file mode 100755
index 0000000..a9c93a2
--- /dev/null
+++ b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/other/DiffMatchPatchCFUtilities.h
@@ -0,0 +1,48 @@
+/*
+ * Diff Match and Patch
+ * Copyright 2018 The diff-match-patch Authors.
+ * https://github.com/google/diff-match-patch
+ *
+ * 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.
+ *
+ * Author: fraser@google.com (Neil Fraser)
+ * ObjC port: jan@geheimwerk.de (Jan Weiß)
+ */
+
+#ifndef _DIFFMATCHPATCHCFUTILITIES_H
+#define _DIFFMATCHPATCHCFUTILITIES_H
+
+CFStringRef diff_CFStringCreateFromUnichar(UniChar ch);
+CFStringRef diff_CFStringCreateJavaSubstring(CFStringRef s, CFIndex begin, CFIndex end);
+
+CFIndex diff_commonPrefix(CFStringRef text1, CFStringRef text2);
+CFIndex diff_commonSuffix(CFStringRef text1, CFStringRef text2);
+CFIndex diff_commonOverlap(CFStringRef text1, CFStringRef text2);
+CFArrayRef diff_halfMatchCreate(CFStringRef text1, CFStringRef text2, const float diffTimeout);
+CFArrayRef diff_halfMatchICreate(CFStringRef longtext, CFStringRef shorttext, CFIndex i);
+
+CFStringRef diff_linesToCharsMungeCFStringCreate(CFStringRef text, CFMutableArrayRef lineArray, CFMutableDictionaryRef lineHash, CFIndex maxLines);
+
+CFIndex diff_cleanupSemanticScore(CFStringRef one, CFStringRef two);
+
+CF_INLINE void diff_CFStringPrepareUniCharBuffer(CFStringRef string, const UniChar **string_chars, UniChar **string_buffer, CFRange string_range) {
+ *string_chars = CFStringGetCharactersPtr(string);
+ if (*string_chars == NULL) {
+ // Fallback in case CFStringGetCharactersPtr() didn’t work.
+ *string_buffer = malloc(string_range.length * sizeof(UniChar));
+ CFStringGetCharacters(string, string_range, *string_buffer);
+ *string_chars = *string_buffer;
+ }
+}
+
+#endif //ifndef _DIFFMATCHPATCHCFUTILITIES_H
diff --git a/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/other/DiffMatchPatch_Prefix.pch b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/other/DiffMatchPatch_Prefix.pch
new file mode 100755
index 0000000..2077572
--- /dev/null
+++ b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/other/DiffMatchPatch_Prefix.pch
@@ -0,0 +1,7 @@
+//
+// Prefix header for all source files of the 'DiffMatchPatch' target in the 'DiffMatchPatch' project.
+//
+
+#ifdef __OBJC__
+ #import
+#endif
diff --git a/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/other/MinMaxMacros.h b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/other/MinMaxMacros.h
new file mode 100755
index 0000000..2765e0f
--- /dev/null
+++ b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/Diff/other/MinMaxMacros.h
@@ -0,0 +1,40 @@
+/*
+ * Diff Match and Patch
+ * Copyright 2018 The diff-match-patch Authors.
+ * https://github.com/google/diff-match-patch
+ *
+ * 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.
+ *
+ * Author: fraser@google.com (Neil Fraser)
+ * ObjC port: jan@geheimwerk.de (Jan Weiß)
+ */
+
+#if !defined(MIN)
+ #define MIN(A,B) \
+ ({__typeof__(A) a = (A); \
+ __typeof__(B) b = (B); \
+ (a < b) ? a : b; })
+#endif
+
+#if !defined(MAX)
+ #define MAX(A,B) \
+ ({__typeof__(A) a = (A); \
+ __typeof__(B) b = (B); \
+ (a > b) ? a : b; })
+#endif
+
+#if !defined(ABS)
+ #define ABS(A) \
+ ({__typeof__(A) a = (A); \
+ (a > 0) ? a : -a; })
+#endif
diff --git a/ProductApp/ProductApp/ProductMain/工具/文本比较/view/GongJuWenBenView.m b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/GongJuWenBenView.m
index 3b30cef..aad7bbf 100644
--- a/ProductApp/ProductApp/ProductMain/工具/文本比较/view/GongJuWenBenView.m
+++ b/ProductApp/ProductApp/ProductMain/工具/文本比较/view/GongJuWenBenView.m
@@ -8,11 +8,18 @@
#import "GongJuWenBenView.h"
#import "FSTextView.h"
+#import "PauseAlterView.h"
+
+
+#import "DiffMatchPatch.h"
+
@interface GongJuWenBenView ()
///
@property (nonatomic , strong) FSTextView *textone;
@property (nonatomic , strong) FSTextView *texttwo;
+
+
@end
@implementation GongJuWenBenView
@@ -49,8 +56,8 @@
make.height.equalTo(viewback).multipliedBy(0.5).offset(-10);
}];
_texttwo = [self drawWenBenView:viewtwo title:@"文本二" tag:1];
- [[viewtwo viewWithTag:1000] setHidden:YES];
- [[viewtwo viewWithTag:1] setHidden:YES];
+// [[viewtwo viewWithTag:1000] setHidden:YES];
+// [[viewtwo viewWithTag:1] setHidden:YES];
UILabel *lbts = [[UILabel alloc] init];
[lbts setText:@"温馨提示:仅支持输入汉字,数字以及标点符号。"];
@@ -72,6 +79,16 @@
}];
[self drawBottomView:viewbottom];
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+ NSString *strtemp = [self getpasteInfo];
+ if(strtemp.length>1)
+ {
+ [PauseAlterView showTitle:@"您已复制文本,是否粘贴?" info:strtemp back:^(NSString * _Nonnull value) {
+ self.textone.text = strtemp;
+ }];
+ }
+ });
+
}
return self;
}
@@ -171,14 +188,32 @@
[btsend addTarget:self action:@selector(bottomAction) forControlEvents:UIControlEventTouchUpInside];
[Tools changedView:btsend colors:MainJBColors startPoint:CGPointMake(0, 0) endPoint:CGPointMake(1, 0)];
}
+-(NSString *)getpasteInfo
+{
+ // 获取通用剪贴板
+ UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
+ // 检查剪贴板是否包含文本
+ if ([pasteboard isKindOfClass:[UIPasteboard class]]) {
+ NSString *clipboardContent = [pasteboard string];
+ if (clipboardContent) {
+ return clipboardContent;
+ } else {
+ return @"";
+ }
+ }
+ else
+ {
+ return @"";
+ }
+}
-(void)clearAction:(UIButton *)sender
{
if(sender.tag==1)
{
self.texttwo.text = @"";
- [[self.texttwo.superview viewWithTag:1000] setHidden:YES];
- [[self.texttwo.superview viewWithTag:1] setHidden:YES];
+// [[self.texttwo.superview viewWithTag:1000] setHidden:YES];
+// [[self.texttwo.superview viewWithTag:1] setHidden:YES];
}
else
{
@@ -188,11 +223,48 @@
-(void)bottomAction
{
- [[self.texttwo.superview viewWithTag:1000] setHidden:NO];
- [[self.texttwo.superview viewWithTag:1] setHidden:NO];
+ NSString *stra = self.textone.text;
+ NSString *strb = self.texttwo.text;
+
+ DiffMatchPatch *dmp = [[DiffMatchPatch alloc]init];
+ NSMutableArray *diffs = [NSMutableArray array];
+ diffs = [dmp diff_mainOfOldString:stra andNewString:strb checkLines:NO];
+ NSMutableAttributedString *attstringA = [[NSMutableAttributedString alloc] initWithString:stra];
+ NSMutableAttributedString *attstringB = [[NSMutableAttributedString alloc] initWithString:strb];
+ [attstringA addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:14] range:NSMakeRange(0, stra.length)];
+
+ [attstringB addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:14] range:NSMakeRange(0, strb.length)];
+
+ NSInteger ilocationA = 0;
+ NSInteger ilocationB = 0;
+ for(Diff *item in diffs)
+ {
+ NSString *strtemp = item.text;
+ if(item.operation == DIFF_INSERT)
+ {
+ [attstringB addAttribute:NSForegroundColorAttributeName value:RGBCOLOR(255, 50, 50) range:NSMakeRange(ilocationB, strtemp.length)];
+ ilocationB+=strtemp.length;
+
+ }
+ else if(item.operation == DIFF_DELETE)
+ {
+ [attstringA addAttribute:NSForegroundColorAttributeName value:RGBCOLOR(255, 50, 50) range:NSMakeRange(ilocationA, strtemp.length)];
+ ilocationA+=strtemp.length;
+ }
+ else
+ {
+ ilocationA+=strtemp.length;
+ ilocationB+=strtemp.length;
+ }
+ }
+
+ self.textone.attributedText = attstringA;
+ self.texttwo.attributedText = attstringB;
+
}
+
@end
diff --git a/ProductApp/ProductApp/ProductMain/工具/智能翻译/view/GongJuFanYiView.m b/ProductApp/ProductApp/ProductMain/工具/智能翻译/view/GongJuFanYiView.m
index 674ea53..1bb6e2e 100644
--- a/ProductApp/ProductApp/ProductMain/工具/智能翻译/view/GongJuFanYiView.m
+++ b/ProductApp/ProductApp/ProductMain/工具/智能翻译/view/GongJuFanYiView.m
@@ -10,6 +10,8 @@
#import "NetWorkManager.h"
#import "LanguageSelectAlterView.h"
+#import "PauseAlterView.h"
+#import "SSENetWorkManager.h"
@interface GongJuFanYiView ()
///
@@ -18,7 +20,7 @@
@property (nonatomic , strong) FSTextView *textview;
@property (nonatomic , strong) UIView *viewout;
-@property (nonatomic , strong) UILabel *lbcontent;
+@property (nonatomic , strong) ViewLable *lbcontent;
///
@property (nonatomic , strong) UILabel *lbone;
@@ -27,6 +29,11 @@
///
@property (nonatomic , strong) NSMutableArray *arrdata;
+///
+@property (nonatomic , strong) SSEConfigModel *modelConfig;
+
+@property (nonatomic , assign) BOOL isDragging;
+@property (nonatomic , assign) BOOL isDraw;
@end
@implementation GongJuFanYiView
@@ -54,6 +61,16 @@
}];
[self drawBottomView:viewbottom];
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+ NSString *strtemp = [self getpasteInfo];
+ if(strtemp.length>1)
+ {
+ [PauseAlterView showTitle:@"您已复制文本,是否粘贴?" info:strtemp back:^(NSString * _Nonnull value) {
+ self.textview.text = strtemp;
+ }];
+ }
+ });
+
[self getCountryData];
}
@@ -113,27 +130,33 @@
make.left.equalTo(viewline.mas_right).offset(6);
}];
- UILabel *lbcontent = [[UILabel alloc] init];
- [lbcontent setText:@"结果"];
- [lbcontent setTextColor:RGBCOLOR(51, 51, 51)];
- [lbcontent setTextAlignment:NSTextAlignmentLeft];
- [lbcontent setFont:[UIFont systemFontOfSize:14]];
- [lbcontent setNumberOfLines:0];
- [view addSubview:lbcontent];
- [lbcontent mas_makeConstraints:^(MASConstraintMaker *make) {
+ ViewLable *lbwz = [[ViewLable alloc] init];
+ [view addSubview:lbwz];
+ [lbwz mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.offset(27);
make.right.equalTo(view).offset(-27);
make.top.equalTo(viewline.mas_bottom).offset(17);
}];
- _lbcontent = lbcontent;
+ lbwz.textColor = RGBCOLOR(61, 61, 61);
+ lbwz.textFont = [UIFont systemFontOfSize:16];
+ [lbwz setBackHeight:^(float fheight, BOOL isend) {
+ if(self.isDragging==NO)
+ {
+ if(self.scvback.contentSize.height-self.scvback.height>0)
+ {
+ [self.scvback setContentOffset:CGPointMake(0, self.scvback.contentSize.height-self.scvback.height)];
+ }
+ }
+ }];
+ _lbcontent = lbwz;
- [lbcontent setUserInteractionEnabled:YES];
+ [lbwz setUserInteractionEnabled:YES];
UILongPressGestureRecognizer *pressl = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(copyTextAction:)];
- [lbcontent addGestureRecognizer:pressl];
+ [lbwz addGestureRecognizer:pressl];
[view mas_makeConstraints:^(MASConstraintMaker *make) {
- make.bottom.equalTo(lbcontent).offset(25);
+ make.bottom.equalTo(lbwz).offset(25);
}];
}
@@ -311,26 +334,47 @@
[btsend addTarget:self action:@selector(bottomAction) forControlEvents:UIControlEventTouchUpInside];
[Tools changedView:btsend colors:MainJBColors startPoint:CGPointMake(0, 0) endPoint:CGPointMake(1, 0)];
}
+-(NSString *)getpasteInfo
+{
+ // 获取通用剪贴板
+ UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
+ // 检查剪贴板是否包含文本
+ if ([pasteboard isKindOfClass:[UIPasteboard class]]) {
+ NSString *clipboardContent = [pasteboard string];
+ if (clipboardContent) {
+ return clipboardContent;
+ } else {
+ return @"";
+ }
+ }
+ else
+ {
+ return @"";
+ }
+}
-(void)clearAction
{
self.textview.text = @"";
}
-(void)copyTextAction:(UIGestureRecognizer *)gesture
{
- UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
- pasteboard.string = self.lbcontent.text;
- [HXHud showMessage:@"复制成功" afterDelayType:0];
+ if(gesture.state == UIGestureRecognizerStateBegan)
+ {
+ UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
+ pasteboard.string = self.lbcontent.strValue;
+ [HXHud showMessage:@"复制成功" afterDelayType:0];
+ }
}
-(void)changeAction
{
- NSString *str = self.lbone.text;
- if([str isEqualToString:@"自动检测"])
- {
- return;
- }
-
- self.lbone.text = self.lbtwo.text;
- self.lbtwo.text = str;
+// NSString *str = self.lbone.text;
+// if([str isEqualToString:@"自动检测"])
+// {
+// return;
+// }
+//
+// self.lbone.text = self.lbtwo.text;
+// self.lbtwo.text = str;
}
-(void)countryAction:(UIButton *)sender
{
@@ -348,11 +392,6 @@
}
}];
}
--(void)bottomAction
-{
- [self.viewout setHidden:NO];
-}
-
-(void)getCountryData
{
[HXLoadingHUD showWithStatus:@"" maskType:0];
@@ -382,4 +421,90 @@
}];
}
+
+-(void)bottomAction
+{
+ if(self.isDraw==YES)
+ {
+ [HXHud showMessage:@"请等待处理完成" afterDelayType:0];
+ return;
+ }
+ if(self.textview.text.length==0)
+ {
+ [HXHud showMessage:self.textview.placeholder afterDelayType:0];
+ return;
+ }
+ [self addtoolsNumber];
+ [self getdata];
+}
+-(void)addtoolsNumber
+{
+ [NetWorkManager requestToolsAdd_countData:self tool_id:[NSString stringWithFormat:@"%d",4] Callback:^(BOOL state, id _Nullable responseObject, NSString * _Nullable describle) {
+
+ }];
+}
+
+-(void)getdata
+{
+ self.isDraw = YES;
+ [LoadAlterView show];
+ ///translate 翻译、get_summary 摘要、get_keyword 关键字、to_sort 缩写、to_long 扩写
+ /*
+ 8 扩写 7缩写 6摘要提取 3提取关键词
+ */
+ NSString *scene = @"translate";
+
+ NSDictionary *dicpush = @{@"content":self.textview.text,
+ @"after":self.lbtwo.text};
+
+ [[SSENetWorkManager shareManager] requestTo:[NSString stringWithFormat:@"https://aiw.batiao8.com/api/tool/completions?scene=%@",scene] dicpush:dicpush config:^(SSEConfigModel * _Nonnull config) {
+ [LoadAlterView dismiss];
+ self.modelConfig = config;
+ [self.viewout setHidden:NO];
+ self.lbcontent.fspeed = self.modelConfig.time.intValue/1000.0;
+ } backValue:^(NSString * _Nonnull value, BOOL isfinish) {
+ if(value.length>3)
+ {
+ [self chuliShuJu:value isfinish:NO];
+ }
+ else
+ {
+ if(isfinish)
+ {
+ [self chuliShuJu:value isfinish:YES];
+ }
+ }
+ } error:^(NSString * _Nonnull errorString) {
+ [LoadAlterView dismiss];
+ [HXHud showMessage:errorString afterDelayType:0];
+ self.isDraw = NO;
+ } ID:^(NSString * _Nonnull value) {
+
+ }];
+}
+-(void)chuliShuJu:(NSString *)value isfinish:(BOOL)isfinish
+{
+ NSLog(@"%@",value);
+ if(isfinish)
+ {
+ self.isDraw = NO;
+ self.lbcontent.isWriteEnd = YES;
+ }
+ else
+ {
+ self.lbcontent.strValue = value;
+ }
+}
+
+
+#pragma mark - UIScrollView
+- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
+{
+ self.isDragging = YES;
+}
+- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
+{
+ self.isDragging = NO;
+}
+
@end
diff --git a/ProductApp/ProductApp/ProductMain/首页/ShouYeViewController.m b/ProductApp/ProductApp/ProductMain/首页/ShouYeViewController.m
index 41045c8..515572a 100644
--- a/ProductApp/ProductApp/ProductMain/首页/ShouYeViewController.m
+++ b/ProductApp/ProductApp/ProductMain/首页/ShouYeViewController.m
@@ -449,6 +449,7 @@
///常用工具
-(void)getTools
{
+ [[NSUserDefaults standardUserDefaults] setObject:@"0" forKey:HomeToosRef];
[NetWorkManager requestToolsFavData:self.view page:1 size:@"100" Callback:^(BOOL state, ToolsListModel *responseObject, NSString * _Nullable describle) {
[self.tableView.mj_header endRefreshing];
if(state)
diff --git a/ProductApp/ProductApp/ProductMain/首页/cell/ShouYeBannerTableViewCell.m b/ProductApp/ProductApp/ProductMain/首页/cell/ShouYeBannerTableViewCell.m
index a69baba..e4dcfba 100644
--- a/ProductApp/ProductApp/ProductMain/首页/cell/ShouYeBannerTableViewCell.m
+++ b/ProductApp/ProductApp/ProductMain/首页/cell/ShouYeBannerTableViewCell.m
@@ -44,7 +44,7 @@
make.edges.equalTo(viewback);
}];
[cleScrollView.layer setMasksToBounds:YES];
- [cleScrollView.layer setCornerRadius:8];
+ [cleScrollView.layer setCornerRadius:17];
[cleScrollView setUserInteractionEnabled:YES];
_cleScrollView = cleScrollView;
diff --git a/ProductApp/ProductApp/ProductMain/首页/cell/ShouYeToolsTableViewCell.m b/ProductApp/ProductApp/ProductMain/首页/cell/ShouYeToolsTableViewCell.m
index 75762f8..57fb9c4 100644
--- a/ProductApp/ProductApp/ProductMain/首页/cell/ShouYeToolsTableViewCell.m
+++ b/ProductApp/ProductApp/ProductMain/首页/cell/ShouYeToolsTableViewCell.m
@@ -162,6 +162,7 @@
{
GongJuTextViewController *vc = [GongJuTextViewController new];
vc.strtitle = model.tool_name;
+ vc.type=model.ID.intValue;
[self.viewController.navigationController pushViewController:vc animated:YES];
}
else if(model.ID.intValue==5)
diff --git a/ProductApp/ProductApp/ProductMain/首页/切换身份/ShouYeLingYuViewController.m b/ProductApp/ProductApp/ProductMain/首页/切换身份/ShouYeLingYuViewController.m
index 4bc9583..2139d42 100644
--- a/ProductApp/ProductApp/ProductMain/首页/切换身份/ShouYeLingYuViewController.m
+++ b/ProductApp/ProductApp/ProductMain/首页/切换身份/ShouYeLingYuViewController.m
@@ -196,6 +196,9 @@
[UserInfoModel shareModel].user_stage = modelson;
[UserInfoModel shareModel].isAllLevel = [NSString stringWithFormat:@"%@",[NSNumber numberWithBool:NO]];
[UserInfoModel shareModel].identityType = @"3";
+
+ [UserInfoModel shareModel].isupdataJiaoyu = YES;
+
[self.navigationController popToRootViewControllerAnimated:YES];
}
else