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