text:
//
//  AnnotationDocument.m
//  Annotation
//
//  Created by Adam Fouse on 9/9/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//

#import "AnnotationDocument.h"
#import "Annotation.h"
#import "AnnotationCategory.h"
#import "VideoProperties.h"
#import "AnnotationXMLParser.h"
#import "AppController.h"
#import "TimeCodedData.h"
#import "TimeSeriesData.h"
#import "TimelineView.h"
#import "MultiTimelineView.h"
#import "DataSource.h"
#import "SenseCamDataSource.h"
#import "TimeCodedImageFiles.h"
#import "NSStringFileManagement.h"
#import "DPConstants.h"
#import "DPViewManager.h"
#import "DPActivityLog.h"
#import "AnnotationSet.h"
#import "VideoDataSource.h"
#import "CompoundDataSource.h"
#import "Sparkle.framework/Headers/SUUpdater.h"
#import <CoreServices/CoreServices.h>

NSString * const MediaChangedNotification = @"MediaChangedNotification";
NSString * const DPMediaAddedKey = @"MediaAdded";
NSString * const DPMediaRemovedKey = @"MediaRemoved";
NSString * const CategoriesChangedNotification = @"CategoriesChangedNotification";;

int const DPCurrentDocumentFormatVersion = 1;

@interface AnnotationDocument (Internal)

- (void)processCategoryChange:(id)sender;
//- (QTMovie*)loadMovieFile:(NSString*)movieFile;
- (BOOL)loadVideoProperties:(VideoProperties*)properties;
- (BOOL)loadDataSource:(DataSource*)dataSource;
- (void)updateMediaFile:(VideoProperties*)props;
- (void)updateDataFile:(DataSource*)source;
- (NSString*)createBackup;

- (BOOL)dataFile:(NSString*)file inSources:(NSArray*)sourcesArray;

+ (NSArray*)defaultCategories;

- (void)handleLoadStateChanged:(QTMovie *)movie;

- (void)saveAnnotationsNow;

@end

@implementation AnnotationDocument

@synthesize modified;
@synthesize activityLog;

+ (AnnotationDocument*)currentDocument
{
	return [AppController currentDoc];
}

- (id)initForVideo:(NSString*)videoFile
{
	NSError *err = nil;
	NSString *defaultAnnotationFile = [[videoFile stringByDeletingPathExtension] stringByAppendingString:@"-Annotations.annotation"];
	// If the default annotation file exists, ask if we should use it

	if(![[NSUserDefaults standardUserDefaults] boolForKey:AFAutomaticAnnotationFileKey])
	{
		NSSavePanel *newAnnotationsPanel = [NSSavePanel savePanel];
		[newAnnotationsPanel setTitle:@"New Annotations File"];
		[newAnnotationsPanel setMessage:@"Please set the name and location for the new annotations file."];
		[newAnnotationsPanel setExtensionHidden:YES];
		[newAnnotationsPanel setCanSelectHiddenExtension:NO];
		[newAnnotationsPanel setRequiredFileType:@"annotation"];

		if ([newAnnotationsPanel runModalForDirectory:nil file:@"New Annotations"] == NSOKButton) {
			return [self initAtFile:[newAnnotationsPanel filename]  withVideo:videoFile];
		}
		else
		{
			[self release];
			return nil;
		}
	}

	if([[NSFileManager defaultManager] fileExistsAtPath:defaultAnnotationFile])
	{
		NSAlert *alert = [[NSAlert alloc] init];
		[alert setMessageText:[NSString stringWithFormat:
							   @"An existing annotation file was found for %@. Do you want to open the existing file or create a new one?",
							   [videoFile lastPathComponent]]];
		[alert addButtonWithTitle:@"Open Existing File"];
		[alert addButtonWithTitle:@"Create New File"];
		NSInteger result = [alert runModal];
		if(result == NSAlertFirstButtonReturn)
		{
			return [self initFromFile:defaultAnnotationFile];
		}
		else
		{
			NSSavePanel *newAnnotationsPanel = [NSSavePanel savePanel];
			[newAnnotationsPanel setTitle:@"New Annotations File"];
			[newAnnotationsPanel setMessage:@"Please set the name and location for the new annotations file."];
			[newAnnotationsPanel setExtensionHidden:YES];
			[newAnnotationsPanel setCanSelectHiddenExtension:NO];
			[newAnnotationsPanel setRequiredFileType:@"annotation"];

			if ([newAnnotationsPanel runModalForDirectory:nil file:@"New Annotations"] == NSOKButton) {
				return [self initAtFile:[newAnnotationsPanel filename]  withVideo:videoFile];
			}
			else
			{
				[self release];
				return nil;
			}
		}

	}
	// If older default annotations exist, update the name and use it
	else if([[NSFileManager defaultManager] fileExistsAtPath:[defaultAnnotationFile stringByDeletingPathExtension]])
	{
		[[NSFileManager defaultManager] moveItemAtPath:[defaultAnnotationFile stringByDeletingPathExtension]
												toPath:defaultAnnotationFile
												 error:&err];
		NSString *videoPropertiesFile = [[defaultAnnotationFile stringByAppendingPathComponent:@"videoInfo.plist"] retain];
		if([[NSFileManager defaultManager] fileExistsAtPath:videoPropertiesFile])
		{
			VideoProperties *properties = [[VideoProperties alloc] initFromFile:videoInfoFile];
			[properties setVideoFile:videoFile];
			[properties saveToFile:videoPropertiesFile];
			[properties release];
			return [self initFromFile:defaultAnnotationFile];
		}
		else
		{
			return [self initAtFile:defaultAnnotationFile withVideo:videoFile];
		}

		//return [self initAtFile:defaultAnnotationFile withVideo:videoFile];
	}
	// Otherwise, create a new default annotation file
	else
	{
		return [self initAtFile:defaultAnnotationFile withVideo:videoFile];
		//return [self initFromFile:defaultAnnotationFile];
	}
}

- (id)initAtFile:(NSString*)filename withVideo:(NSString*)videoFile
{
	NSString *imageDirectory = [filename stringByAppendingPathComponent:@"images"];
	NSString *videoPropertiesFile = [filename stringByAppendingPathComponent:@"videoInfo.plist"];

	if(![[NSFileManager defaultManager] fileExistsAtPath:imageDirectory])
	{
		BOOL result = [[NSFileManager defaultManager] createDirectoryAtPath:imageDirectory withIntermediateDirectories:YES attributes:nil error:nil];
		if(!result)
		{
			NSAlert *alert = [[NSAlert alloc] init];
			[alert setMessageText:[NSString stringWithFormat:@"The file %@ could not be created.",[filename lastPathComponent]]];
			[alert runModal];
			[alert release];
			return nil;
		}
	}

	VideoProperties *properties = [[VideoProperties alloc] initWithVideoFile:videoFile];
	//[properties setVideoFile:videoFile];
	[properties saveToFile:videoPropertiesFile];
	[properties release];

	// Create and save the default categories

	NSArray *defaultCategories = [AnnotationDocument defaultCategories];

	NSString *errorDesc;

	NSData *categoriesData = [NSKeyedArchiver archivedDataWithRootObject:defaultCategories];

    NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:[NSDictionary dictionaryWithObject:categoriesData
																									  forKey:@"DPCategories"]
																   format:NSPropertyListXMLFormat_v1_0
														 errorDescription:&errorDesc];
	if (!plistData) {
        NSLog(@"%@",errorDesc);
        [errorDesc release];
    }
	else
	{
		[plistData writeToFile:[filename stringByAppendingPathComponent:@"properties.plist"] atomically:YES];
	}

	return [self initFromFile:filename];
}

- (id)initFromFile:(NSString*)filename
{
	self = [super init];
	if (self != nil) {
		NSError *err = nil;
		int documentFormatVersion = 0;

        //NSDate *startLoadingDate = [NSDate date];

		// Basic setup
		annotationsDirectory = [filename retain];
		annotationsImageDirectory = [[annotationsDirectory stringByAppendingPathComponent:@"images"] retain];
		videoInfoFile = [[annotationsDirectory stringByAppendingPathComponent:@"videoInfo.plist"] retain];
		annotationsFile = [[annotationsDirectory stringByAppendingPathComponent:@"annotations.xml"] retain];

		NSLog(@"Loading Annotations File: %@", annotationsDirectory);
		if(![[NSFileManager defaultManager] fileExistsAtPath:annotationsImageDirectory])
		{
			[[NSFileManager defaultManager] createDirectoryAtPath:annotationsImageDirectory withIntermediateDirectories:YES attributes:nil error:nil];
		}

		///////////////////////////
		// Load the properties file
		///////////////////////////
		NSLog(@"Loading properties");

		NSString *propertiesFile = [annotationsDirectory stringByAppendingPathComponent:@"properties.plist"];
		if(![[NSFileManager defaultManager] fileExistsAtPath:propertiesFile])
		{
			documentProperties = [[NSMutableDictionary alloc] init];
		}
		else
		{
			NSString *errorDesc = nil;
			NSPropertyListFormat format;
			NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:propertiesFile];
			documentProperties = [(NSDictionary *)[NSPropertyListSerialization
												  propertyListFromData:plistXML
												  mutabilityOption:0
												  format:&format errorDescription:&errorDesc] mutableCopy];

			NSDictionary *userVariables = [documentProperties objectForKey:@"DPUserVariables"];
			if(userVariables)
			{
				[documentProperties setObject:[userVariables mutableCopy] forKey:@"DPUserVariables"];
			}

		}

		documentFormatVersion = [[documentProperties valueForKey:@"DPDocumentFormatVersion"] intValue];
        if(documentFormatVersion > DPCurrentDocumentFormatVersion)
        {
            NSAlert *alert = [[NSAlert alloc] init];
            [alert setMessageText:@"This document can't be opened because it was created with a newer version of ChronoViz."];
            [alert setInformativeText:@"Please update ChronoViz to open this file."];

            [alert addButtonWithTitle:@"OK"];
            [alert addButtonWithTitle:@"Update Now"];

            NSInteger result = [alert runModal];
            [alert release];
            if(result == NSAlertSecondButtonReturn)
            {
                [[SUUpdater sharedUpdater] checkForUpdates:nil];
            }

            [self release];
            return nil;
        }

        savedLayouts = [[documentProperties valueForKey:@"DPSavedLayouts"] mutableCopy];
		if(!savedLayouts)
		{
			savedLayouts = [[NSMutableDictionary alloc] init];
		}
        [documentProperties setValue:savedLayouts
                              forKey:@"DPSavedLayouts"];

		keywords = [[documentProperties valueForKey:@"DPKeywords"] mutableCopy];
		if(!keywords)
		{
			keywords = [[NSMutableArray alloc] init];
		}
        [documentProperties setValue:keywords
                              forKey:@"DPKeywords"];

		categories = [[NSMutableArray alloc] init];

		NSData *categoriesData = [documentProperties valueForKey:@"DPCategories"];

		if(categoriesData)
		{
			NSArray *storedCategories = [NSKeyedUnarchiver unarchiveObjectWithData:categoriesData];
			for(AnnotationCategory* category in storedCategories)
			{
				[self addCategory:category];
			}
		}

		///////////////////////////
		// Load the video info file
		///////////////////////////
        NSLog(@"Loading video info");

		// If the video info file doesn't exist, create one.
		if(![[NSFileManager defaultManager] fileExistsAtPath:videoInfoFile])
		{
			VideoProperties *properties = [[VideoProperties alloc] init];
			[properties saveToFile:videoInfoFile];
			[properties release];
		}

		// If we created one successfully, load it.
		if([[NSFileManager defaultManager] fileExistsAtPath:videoInfoFile])
		{
			videoProperties = [[VideoProperties alloc] initFromFile:videoInfoFile];

			if([videoProperties videoFile] == nil || ([[videoProperties videoFile] length] == 0))
			{
				// Find if a video exists (support for older file format)
				if([filename rangeOfString:@"-Annotations"].location != NSNotFound)
				{
					NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[filename stringByDeletingLastPathComponent] error:&err];
					for(NSString* file in files)
					{
						if ([QTMovie canInitWithFile:file]) {
							[videoProperties setVideoFile:file];
							[videoProperties saveToFile:videoInfoFile];
							break;
						}
					}
				}

				// If there's still no video file, create a placeholder video.
				if([videoProperties videoFile] == nil || ([[videoProperties videoFile] length] == 0))
				{
					NSError *error;
					NSString *movieFile = [annotationsDirectory stringByAppendingPathComponent:@"video.mov"];
					QTMovie *newMovie = [[QTMovie alloc] initToWritableFile:movieFile error:&error];
					NSString* imageName = [[NSBundle mainBundle] pathForResource:@"black" ofType:@"gif"];
					NSImage* imageObj = [[NSImage alloc] initWithContentsOfFile:imageName];

					//QTTime duration = QTMakeTime(6000, 600);

					long long durationtime = [[AppController currentApp] newDocumentDuration];
					long scale = 600;

					QTTime duration = QTMakeTime(durationtime * scale,scale);

					[newMovie addImage:imageObj
						   forDuration:duration
						withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:@"jpeg", QTAddImageCodecType, [NSNumber numberWithLong:scale],QTTrackTimeScaleAttribute,nil]];

					[newMovie updateMovieFile];

					NSDictionary *dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]
																	 forKey:QTMovieFlatten];

					[newMovie writeToFile:movieFile withAttributes:dict];

					[newMovie release];
					[imageObj release];

					[videoProperties setVideoFile:movieFile];
					[videoProperties saveToFile:videoInfoFile];

					if([categories count] == 0)
					{
						NSArray *defaultCategories = [AnnotationDocument defaultCategories];
						for(AnnotationCategory* category in defaultCategories)
						{
							[self addCategory:category];
						}
					}
				}

			}

			[self updateMediaFile:videoProperties];

			QTMovie *movie = [videoProperties loadMovie];
			if(movie)
			{
				[[AppController currentApp] setMovie:movie];
				if([[videoProperties videoFile] rangeOfString:annotationsDirectory].location != NSNotFound)
				{
					[videoProperties setLocalVideo:YES];
				}
			}
			else
			{
				NSLog(@"Error opening base movie");
			}

		} else {
			NSLog(@"Can't find video info file...");
		}

		if([categories count] == 0)
		{
			for(AnnotationCategory* category in [videoProperties categories])
			{
				[self addCategory:category];
			}
		}

		[videoProperties setCategories:[self categories]];

		//////////////////////////////
		// Load additional media files (older method)
		//////////////////////////////
        NSLog(@"Loading media");

		media = [[NSMutableArray alloc] init];
		mediaProperties = [[NSMutableArray alloc] init];
		NSMutableArray *mediaDataSources = [[NSMutableArray alloc] init];
		NSString *mediaPath = [annotationsDirectory stringByAppendingPathComponent:@"media.plist"];
		if((documentFormatVersion == 0) && [[NSFileManager defaultManager] fileExistsAtPath:mediaPath])
		{
			NSArray *storedProperties = [[NSKeyedUnarchiver unarchiveObjectWithFile:mediaPath] retain];
			if(storedProperties)
			{
				for(VideoProperties *properties in storedProperties)
				{
					BOOL success = [self loadVideoProperties:properties];
					if(success)
					{
						VideoDataSource *videoDataSource = [[VideoDataSource alloc] initWithVideoProperties:properties];
						[mediaDataSources addObject:videoDataSource];
						[videoDataSource release];
					}
				}
			}

		}

		////////////////////////
		// Load the annotations
        ////////////////////////
        NSLog(@"Loading annotations");

		if([[NSFileManager defaultManager] fileExistsAtPath:annotationsFile])
		{
			xmlParser = [[AnnotationXMLParser alloc] initWithFile:annotationsFile forDocument:self];
		} else {
			xmlParser = [[AnnotationXMLParser alloc] initForDocument:self];
		}

		// Clear out temporary annotations
		NSMutableArray *tempCategories = [[NSMutableArray alloc] init];
		for(AnnotationCategory *category in categories)
		{
			if([category temporary])
			{
				[tempCategories addObject:category];
				NSArray *annotations = [self annotationsForCategory:category];
				[self removeAnnotations:annotations];
			}
		}
		while([tempCategories count] > 0)
		{
			[self removeCategory:[tempCategories lastObject]];
			[tempCategories removeObject:[tempCategories lastObject]];
		}
		[tempCategories release];

		/////////////////
		// Initialize the data sources and load data sets
        /////////////////
        NSLog(@"Loading data");

		NSString *dataSourcesPath = [annotationsDirectory stringByAppendingPathComponent:@"dataSources.archive"];
		NSString *dataPath = [annotationsDirectory stringByAppendingPathComponent:@"data.archive"];
		if([[NSFileManager defaultManager] fileExistsAtPath:dataSourcesPath])
		{
            if([[[NSFileManager defaultManager] attributesOfItemAtPath:dataSourcesPath error:&err] fileSize] > 10000000L)
            {
                [[AppController currentApp] showDocumentLoading:self];
            }

			dataSources = [[NSKeyedUnarchiver unarchiveObjectWithFile:dataSourcesPath] retain];
			if(!dataSources)
			{
				dataSources = [[NSMutableArray alloc] init];
			}

            if([dataSources count] > 1)
            {
                [[AppController currentApp] showDocumentLoading:self];
            }

			NSMutableArray *dataSourcesCopy = [dataSources copy];
			for(DataSource* dataSource in dataSourcesCopy)
			{
				BOOL success = [self loadDataSource:dataSource];
				if(!success)
				{
					[dataSources removeObject:dataSource];
				}
			}
			[dataSourcesCopy release];

		}
		else if([[NSFileManager defaultManager] fileExistsAtPath:dataPath])
		{
			NSArray *tempData = [[NSKeyedUnarchiver unarchiveObjectWithFile:dataPath] retain];
			if([tempData count] > 0)
			{
				NSAlert *alert = [[NSAlert alloc] init];
				[alert setMessageText:
				 [NSString stringWithFormat:@"The file %@ was created with an older version of ChronoViz, and the time series data format is no longer supported.",[filename lastPathComponent]]];
				[alert setInformativeText:@"Would you like to cancel opening, or remove the old time series data and continue opening?"];
				[alert addButtonWithTitle:@"Open Without Data"];
				[alert addButtonWithTitle:@"Cancel Opening"];

				NSInteger result = [alert runModal];

				if(result != NSAlertFirstButtonReturn)
				{
					[self release];
					return nil;
				}
			}

			dataSources = [[NSMutableArray alloc] init];

			[[NSFileManager defaultManager] removeItemAtPath:dataPath error:&err];

		}
		else
		{
			dataSources = [[NSMutableArray alloc] init];
		}

		[dataSources addObjectsFromArray:mediaDataSources];
		[mediaDataSources release];

		for(Annotation* annotation in [self annotations])
		{
			if([annotation source])
			{
				for(DataSource *dataSource in dataSources)
				{
					if([[annotation source] isEqualToString:[dataSource uuid]] ||
					   [[annotation source] isEqualToString:[dataSource dataFile]] ||
					   [[annotation source] isEqualToString:[dataSource name]])
					{
						[dataSource addAnnotation:annotation];
					}
				}
			}
		}

		if([annotationsDirectory rangeOfString:@"tempannotation"].location == NSNotFound)
		{
			[[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:[NSURL fileURLWithPath:filename]];
		}

		if([[NSUserDefaults standardUserDefaults] boolForKey:AFTrackActivityKey])
		{
            NSData *activityData = [documentProperties valueForKey:@"DPActivityData"];

            if(activityData)
            {
                self.activityLog = (DPActivityLog*)[NSKeyedUnarchiver unarchiveObjectWithData:activityData];
            }
            else
            {
                DPActivityLog *log = [[DPActivityLog alloc] initForDocument:self];
                self.activityLog = log;
                [log release];
            }

		}

	}

	return self;
}

//
// Takes a VideoProperties object that has been loaded from a file on disk
// prepares it for use and adds it to the document
//
- (BOOL)loadVideoProperties:(VideoProperties*)properties
{
	[self updateMediaFile:properties];
	QTMovie *mediaObject = [properties loadMovie];
	if(!mediaObject)
	{
		return NO;
		//[mediaProperties removeObject:properties];
	}
	else
	{
		[mediaObject setAttribute:0 forKey:QTMovieLoopsAttribute];
		[media addObject:mediaObject];
		[mediaProperties addObject:properties];
		[properties setMovie:mediaObject];
		[[AppController currentApp] registerMedia:properties andDisplay:NO];
	}
	return YES;
}

//
// Takes a DataSource object that has been loaded from a file on disk
// Prepares it for use and adds it to the document
//
- (BOOL)loadDataSource:(DataSource*)dataSource
{
	NSLog(@"Data Source: %@ %@",[dataSource name],[dataSource dataFile]);

	if([dataSource isKindOfClass:[CompoundDataSource class]])
	{
		NSArray *sources = [(CompoundDataSource*)dataSource dataSources];
		for(DataSource *source in sources)
		{
			BOOL success = [self loadDataSource:source];
			if(!success)
			{
				[(CompoundDataSource*)dataSource removeDataSource:source];
			}
		}
		return YES;
	}

	if([dataSource isKindOfClass:[VideoDataSource class]])
	{
		return [self loadVideoProperties:[(VideoDataSource*)dataSource videoProperties]];
	}
	else if([dataSource dataFile])
	{
		[self updateDataFile:dataSource];
	}
	else if(![dataSource local])
	{
		[dataSource setDataFile:@""];
		return NO;
	}

//    BOOL showingImages = NO;
	for(TimeCodedData* dataSet in [dataSource dataSets])
	{
		[dataSet setSource:dataSource];

//		if([dataSet isKindOfClass:[TimeCodedImageFiles class]] && !showingImages)
//		{
//            [[[AppController currentApp] viewManager] showDataInMainView:dataSet];
//            showingImages = YES;
//			//[[AppController currentApp] showImageSequence:(TimeCodedImageFiles*)dataSet  inMainWindow:[videoProperties localVideo]];
//		}
	}

	[dataSource load];

	return YES;

}

//- (QTMovie*)loadMovieFile:(NSString*)movieFile
//{
//	BOOL useQuickTimeX = [[NSUserDefaults standardUserDefaults] boolForKey:AFUseQuickTimeXKey];
//
//	if ([QTMovie canInitWithFile:movieFile])
//	{
//		QTMovie *theMovie = nil;
//		NSError *err = nil;
//
//		if(useQuickTimeX)
//		{
//			loaded = NO;
//			SInt32 major = 0;
//			SInt32 minor = 0;
//			Gestalt(gestaltSystemVersionMajor, &major);
//			Gestalt(gestaltSystemVersionMinor, &minor);
//			if ((major == 10 && minor >= 6) || major >= 11) {
//				// For 10.6 or greater
//				NSError *error = nil;
//				NSNumber *loops = [NSNumber numberWithBool:NO];
//				NSNumber *playback = [NSNumber numberWithBool:YES];
//				NSDictionary *attributes =
//				[NSDictionary dictionaryWithObjectsAndKeys:
//				 movieFile, QTMovieFileNameAttribute,
//				 loops, QTMovieLoopsAttribute,
//				 playback, @"QTMovieOpenForPlaybackAttribute",
//				 nil];
//
//				NSLog(@"Opening with QuickTime X");
//
//				theMovie = [[QTMovie alloc] initWithAttributes:attributes
//													  error:&error];
//
//			}
//		}
//
//		// This will be used in 10.5 or if the 10.6 initialization failed
//		if(!theMovie)
//		{
//			NSLog(@"Opening with QuickTime 7");
//
//			theMovie = [[QTMovie movieWithFile:movieFile error:&err] retain];
//
//		}
//
//		[self handleLoadStateChanged:theMovie];
//
//		[[NSNotificationCenter defaultCenter]
//		 addObserver:self
//		 selector:@selector(movieLoadStateChanged:)
//		 name:QTMovieLoadStateDidChangeNotification
//		 object:theMovie];
//
//
//
//		while(!loaded)
//		{
//			[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
//		}
//
//
//		if(theMovie == nil)
//		{
//			NSLog(@"Error loading movie: %@",[err localizedDescription]);
//		}
//
//		return theMovie;
//	}
//	else
//	{
//		NSLog(@"Can't load movie %@",movieFile);
//		return nil;
//	}
//}

- (void) dealloc
{
	[[NSNotificationCenter defaultCenter] removeObserver:self];

	self.activityLog = nil;

	[annotationsDirectory release];
	[annotationsImageDirectory release];
	[videoInfoFile release];
	[annotationsFile release];
	[documentProperties release];
	[videoProperties release];
	[xmlParser release];
	[media release];
	[mediaProperties release];
	[dataSources release];
	[categories release];
	[keywords release];

	[super dealloc];
}

- (QTMovie*)setVideoFile:(NSString*)videoFile
{
    QTMovie *movie = nil;
	if ([QTMovie canInitWithFile:videoFile])
	{
        [videoProperties setMovie:nil];
        [videoProperties setVideoFile:videoFile];
        movie = [videoProperties loadMovie];
        if(!movie)
        {
            NSLog(@"Error loading movie: %@",videoFile);
        }
        else
        {
            [[AppController currentApp] setMovie:movie];
        }

//		NSError *err;
//		movie = [[QTMovie movieWithFile:videoFile error:&err] retain];
//		BOOL success = (movie != nil);
//		if(!success)
//		{
//			NSLog(@"Error loading movie: %@",[err localizedDescription]);
//		}
//		else
//		{
//			if([videoFile rangeOfString:annotationsDirectory].location != NSNotFound)
//			{
//
//			}
//			[videoProperties setVideoFile:videoFile];
//			[[AppController currentApp] setMovie:movie];
//		}
	}
	return movie;
}

- (BOOL)setDuration:(QTTime)duration
{
	if([videoProperties localVideo])
	{
		NSError *error;
		NSString *movieFile = [annotationsDirectory stringByAppendingPathComponent:@"video.mov"];
		NSString *altMovieFile = [annotationsDirectory stringByAppendingPathComponent:@"video1.mov"];
		NSString* imageName = [[NSBundle mainBundle] pathForResource:@"black" ofType:@"gif"];
		NSImage* imageObj = [[NSImage alloc] initWithContentsOfFile:imageName];

		NSString *deleteFile = nil;
		if([movieFile fileExists])
		{
			deleteFile = movieFile;
			movieFile = altMovieFile;
		}
		else if([altMovieFile fileExists])
		{
			deleteFile = altMovieFile;
		}

		QTMovie *newMovie = [[QTMovie alloc] initToWritableFile:movieFile error:&error];

		duration = QTMakeTimeScaled(duration, 600);

		[newMovie addImage:imageObj
			   forDuration:duration
			withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:@"jpeg", QTAddImageCodecType, nil]];

		[newMovie updateMovieFile];

		NSDictionary *dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]
														 forKey:QTMovieFlatten];

		[newMovie writeToFile:movieFile withAttributes:dict];

		[newMovie release];
		[imageObj release];

		[self setVideoFile:movieFile];

		if(deleteFile)
		{
			[deleteFile deleteFile];
		}

		return YES;
	}
	else
	{
		return NO;
	}
}

- (QTTime)duration
{
	return [[self movie] duration];
}

#pragma mark Annotations

- (void)addAnnotation:(Annotation*)annotation
{
	[self addAnnotations:[NSArray arrayWithObject:annotation]];
}

- (void)addAnnotations:(NSArray*)annotations
{
	for(Annotation *annotation in annotations)
	{
		[annotation retain];
		//[self addAnnotation:annotation];
		if([annotation document] && ([annotation document] != self))
		{
			[[annotation document] removeAnnotation:annotation];
		}
		[annotation setDocument:self];
		[xmlParser addAnnotation:annotation];
		[[AppController currentApp] addAnnotation:annotation];
		[annotation release];
	}

	[self saveAnnotations];

}

- (void)removeAnnotation:(Annotation*)annotation
{

	[self removeAnnotations:[NSArray arrayWithObject:annotation]];
}

- (void)removeAnnotations:(NSArray*)annotations
{
	for(Annotation *annotation in annotations)
	{
		[annotation setDocument:nil];
		[[AppController currentApp] removeAnnotation:annotation];
		[xmlParser removeAnnotation:annotation];
	}

	[self saveAnnotations];
}

- (void)saveAnnotations
{
	[xmlParser writeToFile:annotationsFile];
}

- (void)saveAnnotationsNow
{
	[xmlParser writeToFile:annotationsFile waitUntilDone:YES];
}

#pragma mark Categories

+ (NSArray*)defaultCategories
{
	NSMutableArray *defaultCategories = [NSMutableArray array];

	NSArray *defaultColors = [[NSArray alloc] initWithObjects:
							  @"Blue",
							  @"Red",
							  @"Green",
							  @"Orange",
							  @"Yellow",
							  nil];

	int categoryIndex = 1;
	for(NSString *colorName in defaultColors)
	{
		AnnotationCategory *category = [[AnnotationCategory alloc] init];
		category.name = [NSString stringWithFormat:@"Category %i",categoryIndex];
		categoryIndex++;
		category.color = [Annotation colorForString:colorName];
		[defaultCategories addObject:category];
		[category release];
	}

	return defaultCategories;
}

- (AnnotationCategory*)categoryForName:(NSString*)categoryName
{
	for(AnnotationCategory *category in [self categories])
	{
		if([categoryName isEqualToString:[category name]])
		{
			return category;
		}
	}
	return nil;
}

- (AnnotationCategory*)categoryForIdentifier:(NSString*)qualifiedCategoryName
{
	NSArray *components = [qualifiedCategoryName componentsSeparatedByString:DPAltColon];
	if([components count] == 2)
	{
		AnnotationCategory *category = [self categoryForName:[components objectAtIndex:0]];
		return [category valueForName:[components objectAtIndex:1]];
	}
	else if ([components count] == 1)
	{
		return [self categoryForName:[components objectAtIndex:0]];
	}
	else
	{
		return nil;
	}
}

- (AnnotationCategory*)createCategoryForIdentifier:(NSString*)qualifiedCategoryName
{
	NSArray *components = [qualifiedCategoryName componentsSeparatedByString:DPAltColon];
	if([components count] == 2)
	{
		AnnotationCategory *category = [self categoryForName:[components objectAtIndex:0]];
        if(!category)
        {
            category = [self createCategoryWithName:[components objectAtIndex:0]];
        }
		return [category valueForName:[components objectAtIndex:1]];
	}
	else if ([components count] == 1)
	{
        AnnotationCategory *category = [self categoryForName:[components objectAtIndex:0]];
        if(!category)
        {
            category = [self createCategoryWithName:[components objectAtIndex:0]];
        }
        return category;
	}
	else
	{
		return nil;
	}
}

- (AnnotationCategory*)createCategoryWithName:(NSString*)name
{
    if(!name)
    {
        return nil;
    }

	for(AnnotationCategory *existing in [self categories])
	{
		if([name isEqualToString:[existing name]])
		{
			return nil;
		}
	}
	AnnotationCategory *category = [[AnnotationCategory alloc] init];
	[category setName:name];
	[category setColor:[NSColor whiteColor]];
	[self addCategory:category];
	[category release];
	return category;
}

- (AnnotationCategory*)annotationCategoryForKeyEquivalent:(NSString*)key
{
	for(AnnotationCategory *category in [self categories])
	{
		if([[category keyEquivalent] caseInsensitiveCompare:key] == NSOrderedSame)
		{
			return category;
		}
		for(AnnotationCategory *value in [category values])
		{
			if([[value keyEquivalent] caseInsensitiveCompare:key] == NSOrderedSame)
			{
				return value;
			}
		}
	}

	return nil;
}

- (Annotation*)addAnnotationForCategoryKeyEquivalent:(NSString*)key
{
	AnnotationCategory *category = [self annotationCategoryForKeyEquivalent:key];

	if(category)
	{
		Annotation* annotation = [[Annotation alloc] initWithQTTime:[[AppController currentApp] currentTime]];
		[annotation setCategory:category];
		[annotation setAnnotation:@""];
		[self addAnnotation:annotation];
		[annotation release];
		return annotation;
	}
	else
	{
		return nil;
	}
}

- (NSString*)identifierForCategory:(AnnotationCategory*)category
{
	if([category category])
	{
		return [[[self identifierForCategory:[category category]] stringByAppendingString:DPAltColon] stringByAppendingString:[category name]];
	}
	else
	{
		return [category name];
	}
}

- (void)addCategory:(AnnotationCategory*)category
{
	[self addCategory:category atIndex:[categories count]];
//	if(![categories containsObject:category])
//	{
//		[categories addObject:category];
//	}
}

- (void)addCategory:(AnnotationCategory*)category atIndex:(NSInteger)index
{
	if(category
	   && ![categories containsObject:category]
	   && (index <= [categories count]))
	{
		[categories insertObject:category atIndex:index];

		[[NSNotificationCenter defaultCenter] addObserver:self
												 selector:@selector(processCategoryChange:)
													 name:DPCategoryChangeNotification
												   object:category];

		[[NSNotificationCenter defaultCenter]
		 postNotificationName:CategoriesChangedNotification object:self];
	}
}

- (void)moveCategory:(AnnotationCategory*)category toIndex:(NSInteger)index
{
	if(category
	   && [categories containsObject:category]
	   && (index <= [categories count]))
	{
		if(index > [categories indexOfObject:category])
		{
			index--;
		}

		[categories removeObject:category];

		[categories insertObject:category atIndex:index];

		[[NSNotificationCenter defaultCenter]
		 postNotificationName:CategoriesChangedNotification object:self];
	}
}

- (void)removeCategory:(AnnotationCategory*)category
{
	NSArray* annotations = [self annotations];

	for(Annotation* annotation in annotations)
	{
		if([annotation category] == category)
		{
			[annotation setCategory:nil];
			[annotation setColorObject:nil];
			[annotation setUpdated];
		}
	}

	[categories removeObject:category];

	[[NSNotificationCenter defaultCenter]
	 postNotificationName:CategoriesChangedNotification object:self];

}

- (void)saveCategories
{
	//[self saveVideoProperties:videoProperties];
    [self saveDocumentProperties];
}

- (void)processCategoryChange:(id)sender
{
	[[NSNotificationCenter defaultCenter]
	 postNotificationName:CategoriesChangedNotification object:self];
}

#pragma mark Keywords

- (void)addKeyword:(NSString*)keyword
{
	[keywords addObject:keyword];
}

- (void)removeKeyword:(NSString*)keyword
{
	[keywords removeObject:keyword];
}

- (BOOL)keywordExists:(NSString*)keyword
{
	return [keywords containsObject:keyword];
}

#pragma mark Data

- (void)addDataSource:(DataSource*)dataSource
{
	if(![dataSources containsObject:dataSource])
	{
		if([dataSource isKindOfClass:[VideoDataSource class]])
		{
            VideoProperties *properties = [(VideoDataSource*)dataSource videoProperties];
            if(([dataSources count] == 0) && ([[self annotations] count] == 0) &&
               ([[videoProperties videoFile] rangeOfString:annotationsDirectory].location != NSNotFound))
            {
                [properties loadMovie];
                if(![properties hasVideo])
                {
                    [self setDuration:[[properties movie] duration]];
                    [media addObject:[properties loadMovie]];
                    [mediaProperties addObject:properties];
                    [self saveVideoProperties:properties];

                    [[NSNotificationCenter defaultCenter]
                     postNotificationName:MediaChangedNotification
                     object:self
                     userInfo:[NSDictionary dictionaryWithObject:[NSArray arrayWithObject:properties]
                                                          forKey:DPMediaAddedKey]];
                }
                else
                {
                    [properties retain];
                    [videoProperties release];
                    videoProperties = properties;
                    [[AppController currentApp] setMovie:[videoProperties loadMovie]];
                    [self saveVideoProperties:videoProperties];

                    [[NSNotificationCenter defaultCenter]
                     postNotificationName:MediaChangedNotification
                     object:self
                     userInfo:[NSDictionary dictionaryWithObject:[NSArray arrayWithObject:properties]
                                                          forKey:DPMediaAddedKey]];

                    return;
                }
            }
            else
            {
                [media addObject:[properties loadMovie]];
                [mediaProperties addObject:properties];
                [self saveVideoProperties:properties];

                [[NSNotificationCenter defaultCenter]
                 postNotificationName:MediaChangedNotification
                 object:self
                 userInfo:[NSDictionary dictionaryWithObject:[NSArray arrayWithObject:properties]
                                                      forKey:DPMediaAddedKey]];

                [properties addObserver:self forKeyPath:@"title" options:0 context:NULL];
            }
		}

		[dataSources addObject:dataSource];
//		[[NSNotificationCenter defaultCenter] addObserver:self
//												 selector:@selector(saveData)
//													 name:DataSourceUpdatedNotification
//												   object:dataSource];
		[self saveData];

		[[NSNotificationCenter defaultCenter]
		 postNotificationName:DataSetsChangedNotification object:self];
	}
}

- (void)removeDataSource:(DataSource*)dataSource
{
	if([dataSources containsObject:dataSource])
	{
		if([dataSource isKindOfClass:[VideoDataSource class]])
		{
			VideoProperties *properties = [(VideoDataSource*)dataSource videoProperties];
			if([mediaProperties containsObject:properties] && [properties movie])
			{
                [properties retain];
				[media removeObject:[properties movie]];
				[mediaProperties removeObject:properties];
				[self saveMediaFiles];

				[[NSNotificationCenter defaultCenter]
				 postNotificationName:MediaChangedNotification
				 object:self
				 userInfo:[NSDictionary dictionaryWithObject:[NSArray arrayWithObject:properties]
													  forKey:DPMediaRemovedKey]];
                [properties release];
			}
		}

		for(TimeCodedData* dataSet in [dataSource dataSets])
		{
			NSLog(@"Remove data %@",[dataSet name]);
			if([dataSet isKindOfClass:[AnnotationSet class]])
			{
				[self removeAnnotations:[(AnnotationSet*)dataSet annotations]];
			}
			else
			{
				[[[AppController currentApp] viewManager] removeData:dataSet];
			}

		}

		[dataSources removeObject:dataSource];
		[self saveData];

		[[NSNotificationCenter defaultCenter]
		 postNotificationName:DataSetsChangedNotification object:self];
	}
}

- (BOOL)hasDataFile:(NSString*)file
{
	return [self dataFile:file inSources:dataSources];
}

- (BOOL)dataFile:(NSString*)file inSources:(NSArray*)sourcesArray
{
	for(DataSource *source in sourcesArray)
	{
		if([source isKindOfClass:[CompoundDataSource class]])
		{
			if([self dataFile:file inSources:[(CompoundDataSource*)source dataSources]])
			{
				return YES;
			}
		}
		else
		{
			if([[source dataFile] isEqualToString:file])
			{
				return YES;
			}
		}
	}
	return NO;
}

- (CompoundDataSource*)createCompoundDataSource:(NSArray*)sources
{
	if(!sources || [sources count] == 0)
	{
		return nil;
	}

	CompoundDataSource *compound = [[CompoundDataSource alloc] init];
	[compound setName:[[sources objectAtIndex:0] name]];

	for(DataSource *data in sources)
	{
		if([dataSources containsObject:data])
		{
			[compound addDataSource:data];
			[dataSources removeObject:data];
		}
	}

	[self addDataSource:compound];

	[compound release];

	return compound;
}

- (DataSource*)dataSourceForAnnotation:(Annotation*)annotation
{
	if([annotation source])
	{
		for(DataSource *dataSource in dataSources)
		{
			if([[annotation source] isEqualToString:[dataSource uuid]] ||
			   [[annotation source] isEqualToString:[dataSource dataFile]] ||
			   [[annotation source] isEqualToString:[dataSource name]])
			{
				return dataSource;
			}
		}
	}
	return nil;
}

#pragma mark Saving

- (void)save
{
    [self saveData];
	[self saveCategories];

	for(Annotation* annotation in [self annotations])
	{
		[xmlParser updateAnnotation:annotation];
	}
    [self saveAnnotationsNow];

    [self saveVideoProperties:videoProperties];
	[self saveMediaFiles];
	[self saveDocumentProperties];
}

- (void)saveState:(NSData*)stateData
{
	NSError *err;

	[stateData writeToFile:[annotationsDirectory stringByAppendingPathComponent:@"State.data"] options:0 error:&err];
}

- (NSData*)stateData
{
	NSString* stateFile = [annotationsDirectory stringByAppendingPathComponent:@"State.data"];
	if([stateFile fileExists])
	{
		return [NSData dataWithContentsOfFile:stateFile];
	}
	else
	{
		return nil;
	}
}

- (void)saveData
{
	NSString *archivePath = [annotationsDirectory stringByAppendingPathComponent:@"dataSources.archive"];
	[NSKeyedArchiver archiveRootObject:dataSources toFile:archivePath];
}

- (void)saveVideoProperties:(VideoProperties*)properties
{
	if(properties == [self videoProperties])
	{
		[properties saveToFile:[self videoInfoFile]];
	}
	else if([mediaProperties containsObject:properties])
	{
		[self saveMediaFiles];
	}
}

- (void)saveMediaFiles
{
	NSString *archivePath = [annotationsDirectory stringByAppendingPathComponent:@"media.plist"];
	[NSKeyedArchiver archiveRootObject:mediaProperties toFile:archivePath];
}

- (BOOL)saveToPackage:(NSString*)newPath
{
	NSError *err;
	NSFileManager *manager=[NSFileManager defaultManager];
	NSString *currentFileName=annotationsDirectory;
	if ([currentFileName isEqualToString:newPath])
		return NO;
	if ([manager fileExistsAtPath:newPath]
		&& ![manager removeItemAtPath:newPath error:&err])
		return NO;

	//NSFileWrapper *newMain = [[NSFileWrapper alloc] initRegularFileWithContents:[videoProperties serialize]];
    NSFileWrapper *newMain = [[NSFileWrapper alloc] initRegularFileWithContents:[NSKeyedArchiver archivedDataWithRootObject:videoProperties]];
	NSFileWrapper *newFile = [[NSFileWrapper alloc] initDirectoryWithFileWrappers:
							  [NSDictionary dictionaryWithObjectsAndKeys:newMain,@"videoInfo.plist",nil]];

	//save it at the new location
	BOOL returnedFlag=[newFile writeToFile:newPath atomically:YES updateFilenames:NO];

	if(returnedFlag)
	{
		// If we're using a video that's part of the annotation document
		if([[videoProperties videoFile] rangeOfString:annotationsDirectory].location != NSNotFound)
		{
			[[NSFileManager defaultManager] copyItemAtPath:[videoProperties videoFile]
													toPath:[newPath stringByAppendingPathComponent:[[videoProperties videoFile] lastPathComponent]]
													 error:&err];
			[videoProperties setVideoFile:[newPath stringByAppendingPathComponent:[[videoProperties videoFile] lastPathComponent]]];
		}

		[[NSFileManager defaultManager] copyItemAtPath:annotationsImageDirectory
												toPath:[newPath stringByAppendingPathComponent:@"images"]
												 error:&err];

		[annotationsImageDirectory release];
		[videoInfoFile release];
		[annotationsFile release];
		[annotationsDirectory release];
		annotationsDirectory = [newPath retain];
		annotationsImageDirectory = [[annotationsDirectory stringByAppendingPathComponent:@"images"] retain];
		videoInfoFile = [[annotationsDirectory stringByAppendingPathComponent:@"videoInfo.plist"] retain];
		annotationsFile = [[annotationsDirectory stringByAppendingPathComponent:@"annotations.xml"] retain];

		[self save];

		[[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:[NSURL fileURLWithPath:newPath]];
        [[[AppController currentApp] window] setTitle:[[annotationsDirectory lastPathComponent] stringByDeletingPathExtension]];

	}
	else
	{
		NSLog(@"Error saving annotation package.");
	}

	[newFile release];
	[newMain release];
	return returnedFlag;
}

- (void)saveDocumentProperties
{
	NSString *errorDesc;

	[documentProperties setObject:[NSNumber numberWithInt:1] forKey:@"DPDocumentFormatVersion"];

	[documentProperties setObject:annotationsDirectory forKey:@"SavedLocation"];

	NSData *categoriesData = [NSKeyedArchiver archivedDataWithRootObject:[self categories]];

	[documentProperties setObject:categoriesData forKey:@"DPCategories"];

	[documentProperties setObject:[[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBundleVersion"] forKey:@"DPChronoVizVersion"];

    if(self.activityLog && [[NSUserDefaults standardUserDefaults] boolForKey:AFTrackActivityKey])
    {
        NSData *activityData = [NSKeyedArchiver archivedDataWithRootObject:self.activityLog];
        [documentProperties setObject:activityData forKey:@"DPActivityData"];
    }

    NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:documentProperties
																   format:NSPropertyListXMLFormat_v1_0
														 errorDescription:&errorDesc];
	if (!plistData) {
        NSLog(@"%@",errorDesc);
        [errorDesc release];
		return;
    }

	[plistData writeToFile:[annotationsDirectory stringByAppendingPathComponent:@"properties.plist"] atomically:YES];

}

#pragma mark Named States

- (void)saveState:(NSData*)stateData withName:(NSString*)stateName
{
    [savedLayouts setObject:stateData forKey:stateName];
}

- (NSArray*)savedStates
{
    return [savedLayouts allKeys];
}

- (NSData*)stateForName:(NSString*)stateName
{
    return [savedLayouts objectForKey:stateName];
}

- (void)removeStateNamed:(NSString*)stateName
{
    [savedLayouts removeObjectForKey:stateName];
}

#pragma mark Updating Paths

- (void)updateMediaFile:(VideoProperties*)props
{
	NSString *videoFile = [props videoFile];

    NSString *newFile = nil;
    if([props localVideo]
       && ![videoFile fileExists])
    {
        newFile = [[self annotationsDirectory] stringByAppendingPathComponent:[videoFile lastPathComponent]];
        if(![newFile fileExists])
        {
            newFile = nil;
        }
    }
    else
    {
        newFile = [self resolveFile:videoFile];
    }

	if(!newFile)
	{
		newFile = [videoFile stringByAskingForReplacement];
	}

	if(newFile != videoFile)
	{
		[props setVideoFile:newFile];
	}

}

- (void)updateDataFile:(DataSource*)source
{
	NSString *dataFile = [source dataFile];
	NSString *newFile = [self resolveFile:dataFile];

	if(!newFile)
	{
		if([source directoryDataFile])
		{
			newFile = [dataFile stringByAskingForReplacementDirectory];
		}
		else
		{
			newFile = [dataFile stringByAskingForReplacement];
		}
	}

	if(newFile != dataFile)
	{
		[source setDataFile:newFile];
	}
}

- (NSString*)resolveFile:(NSString*)file
{
	if([file fileExists])
	{
		return file;
	}
	else if([file rangeOfString:@"/Dropbox/"].location != NSNotFound)
	{

		NSString *relativePath = [@"~" stringByAppendingString:[file substringFromIndex:[file rangeOfString:@"/Dropbox/"].location]];
		NSString *newFile = [relativePath stringByStandardizingPath];
		NSLog(@"Updating Dropbox File: %@",newFile);
		if([newFile fileExists])
		{
			return newFile;
		}

	}
	else if([documentProperties objectForKey:@"SavedLocation"])
	{
		NSString *savedLocation = [documentProperties objectForKey:@"SavedLocation"];
		NSString *relativePath = [file relativePathFromBaseDirPath:savedLocation];
		NSString *newPath = [relativePath absolutePathFromBaseDirPath:annotationsDirectory];

		NSLog(@"Checking relative path...");
		NSLog(@"Original path: %@",file);
		NSLog(@"Relative path: %@",relativePath);
		NSLog(@"New path: %@",newPath);

		if([newPath fileExists])
		{
			return newPath;
		}

	}

	return nil;
}

#pragma mark Accessors

- (NSArray*)annotations
{
	return [xmlParser annotations];
}

- (NSArray*)annotationsForCategory:(AnnotationCategory*)category
{
	NSMutableArray *results = [NSMutableArray array];
	for(Annotation* annotation in [xmlParser annotations])
	{
		if([[annotation categories] containsObject:category])
		{
			[results addObject:annotation];
		}
	}
	return results;
}

- (NSArray*)categories
{
	return categories;
}

- (NSArray*)keywords
{
	return keywords;
}

- (NSString*)annotationsDirectory
{
	return annotationsDirectory;
}

- (NSString*)annotationsImageDirectory
{
	return annotationsImageDirectory;
}

- (NSString*)annotationsFile
{
	return annotationsFile;
}

- (NSString*)videoInfoFile
{
	return videoInfoFile;
}

- (VideoProperties*)videoProperties
{
	return videoProperties;
}

- (AnnotationXMLParser*)xmlParser
{
	return xmlParser;
}

- (NSArray*)dataSets
{
	NSMutableArray *dataSets = [NSMutableArray array];

	for(DataSource *source in dataSources)
	{
		[dataSets addObjectsFromArray:[source dataSets]];
	}

	return dataSets;

	//return data;
}

- (NSArray*)dataSetsOfClass:(Class)dataSetClass
{
	NSMutableArray *set = [NSMutableArray array];
	for(TimeCodedData* dataSet in [self dataSets])
	{
		if([dataSet isKindOfClass:dataSetClass])
		{
			[set addObject:dataSet];
		}
	}
	return set;
}

- (NSArray*)timeSeriesData
{
	return [self dataSetsOfClass:[TimeSeriesData class]];

//	NSMutableArray *timeSeriesData = [NSMutableArray array];
//	for(TimeCodedData* dataSet in [self dataSets])
//	{
//		if([dataSet isKindOfClass:[TimeSeriesData class]])
//		{
//			[timeSeriesData addObject:dataSet];
//		}
//	}
//	return timeSeriesData;
}

- (NSArray*)dataSources
{
	return dataSources;
}

- (QTMovie*)movie
{
	return [videoProperties loadMovie];
}

- (NSArray*)media
{
	return media;
}

- (NSArray*)mediaProperties
{
	return mediaProperties;
}

- (NSArray*)allMediaProperties
{
    return [[NSArray arrayWithObject:videoProperties] arrayByAddingObjectsFromArray:mediaProperties];
}

- (NSMutableDictionary*)documentVariables
{
	NSMutableDictionary *userVariables = [documentProperties objectForKey:@"DPUserVariables"];
	if(!userVariables)
	{
		userVariables = [NSMutableDictionary dictionary];
		[documentProperties setObject:userVariables forKey:@"DPUserVariables"];
	}
	return userVariables;
}

- (NSDate*)startDate
{
	return [videoProperties startDate];
}

- (void)observeValueForKeyPath:(NSString *)keyPath
					  ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    if ([keyPath isEqual:@"title"])
	{
		[[NSNotificationCenter defaultCenter]
		 postNotificationName:MediaChangedNotification object:self];
    }
	else
	{
		[super observeValueForKeyPath:keyPath
							 ofObject:object
							   change:change
							  context:context];
	}
}

#pragma mark Movie Loading

-(void)handleLoadStateChanged:(QTMovie *)theMovie
{
    NSInteger loadState = [[theMovie attributeForKey:QTMovieLoadStateAttribute] longValue];

    if (loadState == QTMovieLoadStateError) {
		 /* NSError *err = [movie attributeForKey:QTMovieLoadStateErrorAttribute]; */
		NSLog(@"Load state error");
    }

    if (loadState >= QTMovieLoadStateLoaded) {
        /* can query properties here */
        /* for instance, if you need to size a QTMovieView based on the movie's natural size, you can do so now */
        /* you can also put the movie into a view now, even though no media data might yet be available and hence
		 nothing will be drawn into the view */
		loaded = YES;
    }

    if (loadState >= QTMovieLoadStatePlayable) {
        /* can start movie playing here */
    }
}

-(void)movieLoadStateChanged:(NSNotification *)notification
{
    QTMovie *theMovie = (QTMovie *)[notification object];

    if (theMovie) {
        [self handleLoadStateChanged:theMovie];
    }
}

@end


text:
package com.javarush.test.level07.lesson09.task05;

import java.util.ArrayList;
import java.util.Scanner;

/* Удвой слова
1. Введи с клавиатуры 10 слов в список строк.
2. Метод doubleValues должен удваивать слова по принципу a,b,c -> a,a,b,b,c,c.
3. Используя цикл for выведи результат на экран, каждое значение с новой строки.
*/

public class Solution
{
    public static void main(String[] args) throws Exception {
        ArrayList<String> list = new ArrayList<String>();
        Scanner scan = new Scanner(System.in);
        for (int i=0; i<10; i++)
            list.add(scan.nextLine());
        ArrayList<String> result = doubleValues(list);

        for (String aResult : result)
            System.out.println(aResult);
    }

    public static ArrayList<String> doubleValues(ArrayList<String> list) {
        for (int i=0; i<list.size();) {
            list.add(i+1, list.get(i));
            i+=2;
        }
        return list;
    }
}

redpajama_set_name: RedPajamaGithub


text:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.sorcix</groupId>
    <artifactId>sirc</artifactId>
    <version>1.1.6-SNAPSHOT</version>
    <name>sIRC</name>
    <description>Sorcix Lib-IRC (sIRC) is a simple IRC library for Java.</description>
    <url>http://github.com/sorcix/sIRC/</url>
    <packaging>jar</packaging>

    <developers>
        <developer>
            <id>sorcix</id>
            <name>Vic Demuzere</name>
            <email>vic@demuzere.be</email>
            <url>http://vic.demuzere.be</url>
        </developer>
    </developers>
    <licenses>
        <license>
            <name>The MIT License</name>
            <distribution>repo</distribution>
            <url>http://www.opensource.org/licenses/mit-license.php</url>
        </license>
    </licenses>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <scm>
        <connection>scm:git:https://github.com/sorcix/sIRC.git</connection>
        <developerConnection>scm:git:git@github.com:sorcix/sIRC.git</developerConnection>
        <url>https://github.com/sorcix/sIRC</url>
    </scm>

    <parent>
        <groupId>org.sonatype.oss</groupId>
        <artifactId>oss-parent</artifactId>
        <version>7</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.8.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>1.8.5</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>${basedir}</directory>
                <includes>
                    <include>LICENSE</include>
                </includes>
                <targetPath>META-INF/</targetPath>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.1</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <version>2.1.2</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-javadoc-plugin</artifactId>
                <version>2.7</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <reporting>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jxr-plugin</artifactId>
                <version>2.2</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-javadoc-plugin</artifactId>
                <version>2.7</version>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>findbugs-maven-plugin</artifactId>
                <version>2.3.1</version>
                <configuration>
                    <effort>Max</effort>
                    <threshold>Low</threshold>
                </configuration>
            </plugin>
        </plugins>
    </reporting>
</project>


text:
package org.kairosdb.rollup;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.inject.name.Named;
import org.kairosdb.core.KairosDBService;
import org.kairosdb.core.Main;
import org.kairosdb.core.datastore.Duration;
import org.kairosdb.core.datastore.KairosDatastore;
import org.kairosdb.core.datastore.QueryMetric;
import org.kairosdb.core.datastore.TimeUnit;
import org.kairosdb.core.exception.KairosDBException;
import org.kairosdb.core.scheduler.KairosDBScheduler;
import org.kairosdb.eventbus.FilterEventBus;
import org.quartz.DateBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobKey;
import org.quartz.Trigger;
import org.quartz.impl.JobDetailImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.locks.ReentrantLock;

import static java.util.Objects.requireNonNull;
import static com.google.common.base.Preconditions.checkState;
import static org.kairosdb.util.Preconditions.requireNonNullOrEmpty;
import static org.quartz.CalendarIntervalScheduleBuilder.calendarIntervalSchedule;
import static org.quartz.TriggerBuilder.newTrigger;

/**
 * Managers the scheduling of roll-ups.
 */
public class SchedulingManager implements KairosDBService
{
    public static final Logger logger = LoggerFactory.getLogger(SchedulingManager.class);

    private static final String GROUP_ID = RollUpJob.class.getSimpleName();
    private static final String DELAY = "kairosdb.rollups.server_assignment.check_update_delay_millseconds";

    private final String hostName;
    private final String serverGuid;
    private final KairosDBScheduler scheduler;
    private final KairosDatastore dataStore;
    private final RollUpAssignmentStore assignmentStore;
    private final RollUpTasksStore taskStore;
    private final ScheduledExecutorService executorService;
    private final FilterEventBus eventBus;
    private final ReentrantLock lock = new ReentrantLock();
    private final RollupTaskStatusStore statusStore;

    private long assignmentsLastModified;
    private long rollupsLastModified;
    private Set<String> assignmentsCache = new HashSet<>();
    private Map<String, RollupTask> tasksCache = new HashMap<>();

    @Inject
    public SchedulingManager(RollUpTasksStore taskStore, RollUpAssignmentStore assignmentStore,
            KairosDBScheduler scheduler, KairosDatastore dataStore,
            @Named(RollUpModule.ROLLUP_EXECUTOR)ScheduledExecutorService executorService,
            FilterEventBus eventBus,
            RollupTaskStatusStore statusStore,
            @Named(DELAY) long delay,
            @Named("HOSTNAME") String hostName,
            @Named(Main.KAIROSDB_SERVER_GUID) String guid)
            throws RollUpException
    {
        this.taskStore = requireNonNull(taskStore, "taskStore cannot be null");
        this.scheduler = requireNonNull(scheduler, "scheduler cannot be null");
        this.dataStore = requireNonNull(dataStore, "dataStore cannot be null");
        this.assignmentStore = requireNonNull(assignmentStore, "assignmentStore cannot be null");
        this.hostName = requireNonNullOrEmpty(hostName, "hostname cannot be null or empty");
        this.executorService = requireNonNull(executorService, "executorService cannot be null or empty");
        this.eventBus = requireNonNull(eventBus, "eventBus cannot be null");
        this.statusStore = requireNonNull(statusStore, "statusStore cannot be null");
        this.serverGuid = requireNonNullOrEmpty(guid, "guid cannot be null or empty");

        // Start thread that checks for rollup changes and rollup assignments
        executorService.scheduleWithFixedDelay(new CheckChanges(), 0, delay, java.util.concurrent.TimeUnit.MILLISECONDS);
    }

    private class CheckChanges implements Runnable
    {
        @Override
        public void run()
        {
            checkSchedulingChanges();
        }
    }

    @VisibleForTesting
    void checkSchedulingChanges()
    {
        try {
            long assignmentTime = assignmentStore.getLastModifiedTime();
            long taskStoreTime = taskStore.getLastModifiedTime();
            if (haveRollupsOrAssignmentsChanged(assignmentTime, taskStoreTime)) {
                logger.debug("Checking for roll-up scheduling changes...");

                Set<String> currentAssignments = getAssignmentsCache();
                Map<String, RollupTask> tasks = taskStore.read();
                Set<String> myAssignmentIds = getMyAssignmentIds(serverGuid, assignmentStore.getAssignments());

                // Schedule modified tasks
                rescheduleModifiedTasks(tasks);                                                         // unschedule and then reschedule changed tasks
                removeScheduledTasks(
                        Sets.union(Sets.difference(currentAssignments, tasks.keySet()),                 // unschedule removed tasks or tasks not longer assigned to me
                                Sets.difference(currentAssignments, myAssignmentIds)));

                // Schedule new assignments
                scheduleNewTasks(Sets.difference(myAssignmentIds, currentAssignments));                 // schedule newly assigned tasks

                // Update caches
                lock.lock();
                try {
                    assignmentsCache = myAssignmentIds;
                    assignmentsLastModified = assignmentTime;
                    rollupsLastModified = taskStoreTime;
                    tasksCache = tasks;
                }
                finally {
                    lock.unlock();
                }
            }
        }
        catch (Throwable e) {
            logger.error("Failed to modify roll-up scheduling", e);
        }
    }

    private Set<String> getAssignmentsCache()
    {
        lock.lock();
        try
        {
            return ImmutableSet.copyOf(assignmentsCache);
        }
        finally {
            lock.unlock();
        }
    }

    private Set<String> getMyAssignmentIds(String guid, Map<String, String> assignments)
    {
        Set<String> myIds = new HashSet<>();
        for (String id : assignments.keySet()) {
            if (assignments.get(id).equals(guid))
            {
                myIds.add(id);
            }
        }
        return myIds;
    }

    private boolean haveRollupsOrAssignmentsChanged(long assignmentTime, long taskStoreTime)
            throws RollUpException
    {
        lock.lock();
        try {
            return assignmentsLastModified == 0 ||
                    rollupsLastModified == 0 ||
                    assignmentsLastModified != assignmentTime ||
                    rollupsLastModified != taskStoreTime;
        }
        finally {
            lock.unlock();
        }
    }

    private void rescheduleModifiedTasks(Map<String, RollupTask> tasks)
    {
        for (String id : tasks.keySet()) {
            RollupTask existingTask = tasksCache.get(id);
            if (existingTask != null && tasks.get(id).getLastModified() != existingTask.getLastModified()) {
                updateScheduledTask(tasks.get(id));
            }
        }
    }

    private void scheduleNewTasks(Set<String> ids)
    {
        for (String id : ids) {
            try {
                RollupTask task = taskStore.read(id);
                if (task != null) {
                    Trigger trigger = createTrigger(task);
                    JobDetailImpl jobDetail = createJobDetail(task, dataStore, hostName, eventBus, statusStore);
                    scheduler.schedule(jobDetail, trigger);
                    updateStatus(task, trigger.getNextFireTime());
                    logger.info("Scheduled roll-up task " + task.getName() + " with id " + jobDetail.getFullName() + ". Next execution time " + trigger.getNextFireTime());
                }
                else {
                    logger.error("A roll-up task does not exist for id: " + id);
                }
            }
            catch (RollUpException e) {
                logger.error("Could not read task for id " + id, e);
            }
            catch (KairosDBException e) {
                logger.error("Failed to schedule new roll up task job " + id, e);
            }
        }
    }

    private void updateScheduledTask(RollupTask task)
    {
        try {
            scheduler.cancel(getJobKey(task.getId()));
        }
        catch (KairosDBException e) {
            logger.error("Could not cancel roll up task job " + task, e);
            return;
        }

        try {
            logger.info("Updating schedule for rollup " + task.getName());
            JobDetailImpl jobDetail = createJobDetail(task, dataStore, hostName, eventBus, statusStore);
            Trigger trigger = createTrigger(task);
            scheduler.schedule(jobDetail, trigger);
            logger.info("Roll-up task " + task.getName() + " with id " + jobDetail.getKey() + " scheduled. Next execution time " + trigger.getNextFireTime());
        }
        catch (KairosDBException e) {
            logger.error("Could not schedule roll up task job " + task, e);
        }
    }

    private void removeScheduledTasks(Set<String> ids)
    {
        for (String id : ids) {
            try {
                JobKey jobKey = getJobKey(id);
                logger.info("Cancelling rollup " + id);
                scheduler.cancel(jobKey);
            }
            catch (RollUpException e) {
                logger.error("Could not read task for id " + id, e);
            }
            catch (KairosDBException e) {
                logger.error("Could not cancel roll up task job " + id, e);
            }
        }
    }

    private void updateStatus(RollupTask task, Date nextExecutionTime)
    {
        try {
            RollupTaskStatus status = getOrCreateStatus(task, nextExecutionTime);

            if (status.getStatuses().isEmpty() && !task.getRollups().isEmpty() && !task.getRollups().get(0).getQueryMetrics().isEmpty()) {
                // Add empty status
                QueryMetric metric = task.getRollups().get(0).getQueryMetrics().get(0);
                status.addStatus(RollupTaskStatus.createQueryMetricStatus(metric.getName(), 0, 0, 0));
            }
            statusStore.write(task.getId(), status);
        }
        catch (RollUpException e) {
            logger.error("Could not update status.", e);
        }
    }

    private RollupTaskStatus getOrCreateStatus(RollupTask task, Date nextExecutionTime)
            throws RollUpException
    {
        RollupTaskStatus status = statusStore.read(task.getId());
        if (status == null) {
            return new RollupTaskStatus(nextExecutionTime, hostName);
        }
        status.setNextScheduled(nextExecutionTime);
        return status;
    }

    private static JobKey getJobKey(String id)
    {
        return new JobKey(id, GROUP_ID);
    }

    @VisibleForTesting
    static JobDetailImpl createJobDetail(RollupTask task, KairosDatastore dataStore, String hostName, FilterEventBus eventBus, RollupTaskStatusStore statusStore)
    {
        JobDetailImpl jobDetail = new JobDetailImpl();
        jobDetail.setJobClass(RollUpJob.class);
        jobDetail.setKey(getJobKey(task.getId()));

        JobDataMap map = new JobDataMap();
        map.put("task", task);
        map.put("datastore", dataStore);
        map.put("hostName", hostName);
        map.put("eventBus", eventBus);
        map.put("statusStore", statusStore);
        jobDetail.setJobDataMap(map);
        return jobDetail;
    }

    @VisibleForTesting
    @SuppressWarnings("ConstantConditions")
    static Trigger createTrigger(RollupTask task)
    {
        Duration executionInterval = task.getExecutionInterval();
        return newTrigger()
                .withIdentity(task.getId(), GROUP_ID)
                .startAt(DateBuilder.futureDate((int) executionInterval.getValue(), toIntervalUnit(executionInterval.getUnit())))
                .withSchedule(calendarIntervalSchedule()
                        .withInterval((int) executionInterval.getValue(), toIntervalUnit(executionInterval.getUnit())))
                .build();
    }

    private static DateBuilder.IntervalUnit toIntervalUnit(TimeUnit unit)
    {
        switch (unit) {
            case MILLISECONDS:
                return DateBuilder.IntervalUnit.MILLISECOND;
            case SECONDS:
                return DateBuilder.IntervalUnit.SECOND;
            case MINUTES:
                return DateBuilder.IntervalUnit.MINUTE;
            case HOURS:
                return DateBuilder.IntervalUnit.HOUR;
            case DAYS:
                return DateBuilder.IntervalUnit.DAY;
            case WEEKS:
                return DateBuilder.IntervalUnit.WEEK;
            case MONTHS:
                return DateBuilder.IntervalUnit.MONTH;
            case YEARS:
                return DateBuilder.IntervalUnit.YEAR;
            default:
                checkState(false, "Invalid time unit" + unit);
                return null;
        }
    }

    @Override
    public void start()
            throws KairosDBException
    {
    }

    @Override
    public void stop()
    {
        executorService.shutdown();
    }
}


text:
/**
 * This file contains all necessary Angular controller definitions for 'frontend.admin.login-history' module.
 *
 * Note that this file should only contain controllers and nothing else.
 */
(function () {
  'use strict';

  angular.module('frontend.snapshots')
    .controller('SnapshotController', [
      '_', '$scope', '$stateParams', '$rootScope', '$q', '$log', '$ngBootbox',
      'SocketHelperService', 'MessageService', 'SnapshotsService',
      '$state', '$uibModal', 'DialogService', 'Snapshot', 'AuthService',
      function controller(_, $scope, $stateParams, $rootScope, $q, $log, $ngBootbox,
                          SocketHelperService, MessageService, SnapshotsService,
                          $state, $uibModal, DialogService, Snapshot, AuthService) {

        $scope.token = AuthService.token();

        $scope.showRestoreModal = function () {

          var modalInstance = $uibModal.open({
            animation: true,
            backdrop: 'static',
            ariaLabelledBy: 'modal-title',
            ariaDescribedBy: 'modal-body',
            size: 'lg',

            templateUrl: 'js/app/snapshots/views/snapshot-apply-modal.html',
            controller: function ($scope, $uibModalInstance, SnapshotsService, UserService, _snapshot) {

              $scope.user = UserService.user()

              $scope.ready = false;
              $scope.imports = []

              $scope.objects = {}
              Object.keys(_snapshot.data).forEach(function (item) {
                $scope.objects[item] = {
                  isChecked: false
                };
              })

              $scope.updateImports = function () {
                $scope.imports = []
                Object.keys($scope.objects).forEach(function (key) {
                  if ($scope.objects[key].isChecked) {
                    $scope.imports.push(key)
                  }
                });
              }

              $scope.close = function () {
                $uibModalInstance.dismiss()
              }

              $scope.selectNode = function () {
                $uibModal.open({
                  animation: true,
                  ariaLabelledBy: 'modal-title',
                  ariaDescribedBy: 'modal-body',
                  templateUrl: 'js/app/connections/connections-modal.html',
                  controller: 'UpdateUserNodeController',
                  controllerAs: '$ctrl',
                  resolve: {
                    _nodes: [
                      '_',
                      'ListConfig', 'SocketHelperService',
                      'NodeModel',
                      function resolve(
                        _,
                        ListConfig, SocketHelperService,
                        NodeModel
                      ) {
                        return NodeModel.load({
                          sort: 'createdAt DESC'
                        });
                      }
                    ]
                  }
                });
              }

              $scope.restore = function () {
                $scope.ready = true;
                $scope.restoring = true;
                SnapshotsService.restoreSnapshot(_snapshot.id, $scope.imports)
                  .then(function (success) {
                    $scope.results = success.data
                    $scope.restoring = false;
                  })
                  .catch(function (err) {
                    console.error("restoreSnapshot:error", err)
                    $scope.restoring = false;
                    if (err.data && err.data.message) {
                      MessageService.error(err.data ? (err.data.message || 'Undefined error') : 'Server error');
                    }
                  });
              }

              //restore()

            },
            resolve: {
              _snapshot: function () {
                return $scope.originalSnapshot
              }
            }
          });

          modalInstance.result.then(function (d) {

          }, function (result) {

          });

        }

        function _fetchData() {

          $scope.loading = true;

          Snapshot.fetch($stateParams.id)
            .then(function (result) {

              $scope.originalSnapshot = result;
              $scope.snapshot = _.cloneDeep(result);

              // $scope.snapshotCopy = angular.copy(_snapshot)
              // delete $scope.snapshotCopy.data;
              // $scope.snapshot = $scope.snapshotCopy

              // Hide the orderlist attribute of upstreams for faster rendering
              if ($scope.snapshot.data.upstreams) {
                $scope.snapshot.data.upstreams.forEach(function (item) {
                  item.orderlist = '( Not shown for faster DOM rendering... )'
                })
              }

              $scope.loading = false;

            }).catch(function (err) {
            $scope.loading = false;

          })

        }

        _fetchData();

      }
    ])
  ;
}());


text:
package com.ctrip.xpipe.redis.ctrip.integratedtest.console.migration;

import com.ctrip.xpipe.api.command.CommandFuture;
import com.ctrip.xpipe.command.DefaultCommandFuture;
import com.ctrip.xpipe.redis.console.AbstractConsoleIntegrationTest;
import com.ctrip.xpipe.redis.console.controller.api.migrate.meta.BeaconMigrationRequest;
import com.ctrip.xpipe.redis.console.controller.api.migrate.meta.BeaconMigrationResponse;
import com.ctrip.xpipe.redis.console.model.ClusterTbl;
import com.ctrip.xpipe.redis.console.service.ClusterService;
import com.ctrip.xpipe.spring.WebClientFactory;
import com.ctrip.xpipe.utils.XpipeThreadFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.resources.ConnectionProvider;

import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;

/**
 * @author lishanglin
 * date 2021/10/21
 */
public class ConsoleContinueMigrationTest extends AbstractConsoleIntegrationTest {

    private int qps = 30;

    private AtomicInteger continueTimes = new AtomicInteger(0);

    private ScheduledExecutorService scheduled;

    private Queue<String> waitMigrationClusters = new ConcurrentLinkedQueue<>();

    private WebClient client;

    private String sourceDc = "PTOY";

    private String targetDc = "PTJQ";

    private String console = "http://10.2.39.18:8080";

    @Autowired
    private ClusterService clusterService;

    @Before
    public void setupContinueMigrationTest() throws Exception {
        scheduled = Executors.newScheduledThreadPool(1, XpipeThreadFactory.create(getTestName()));
        client = WebClientFactory.makeWebClient(3000, 30000,
                loopResources, ConnectionProvider.builder("TestConnProvider").maxConnections(1000)
                .pendingAcquireTimeout(Duration.ofMillis(1000)).maxIdleTime(Duration.ofMillis(10000)).build());

        initClustersFromDb();
    }

    @Test
    public void testContinueMigration() throws Exception {
        scheduled.scheduleWithFixedDelay(this::migrateMultiClusters, 0, 1, TimeUnit.SECONDS);
        waitConditionUntilTimeOut(waitMigrationClusters::isEmpty, 2400000, 10000);
    }

    @Test
    public void testContinueMigrationToAndBack() throws Exception {
        this.continueTimes.set(10);
        scheduled.scheduleWithFixedDelay(this::migrateMultiClusters, 0, 1, TimeUnit.SECONDS);
        waitConditionUntilTimeOut(() -> 0 == continueTimes.get(), 2400000, 10000);
    }

    private void switchTargetDc() {
        String tmpTarget = this.targetDc;
        this.targetDc = this.sourceDc;
        this.sourceDc = tmpTarget;
    }

    private void initClustersFromDb() {
        List<ClusterTbl> clusterTbls = clusterService.findActiveClustersByDcName(sourceDc);
        clusterTbls.forEach(clusterTbl -> waitMigrationClusters.add(clusterTbl.getClusterName()));
    }

    private void initClusters() {
        IntStream.rangeClosed(0, 20000).forEach(i -> {
            String cluster = "cluster" + i;
            waitMigrationClusters.add(cluster);
        });
    }

    private void migrateMultiClusters() {
        for (int i = 0; i < qps; i++) {
            if (!migrateNextCluster()) break;
        }
        if (waitMigrationClusters.isEmpty() && continueTimes.get() > 0) {
            continueTimes.decrementAndGet();
            switchTargetDc();
            initClustersFromDb();
        }
    }

    private boolean migrateNextCluster() {
        String cluster = waitMigrationClusters.poll();
        if (null == cluster) {
            logger.info("[migrateNextCluster] clusters clear");
            return false;
        }

        reqMigration(cluster, targetDc).addListener(migFuture -> {
            if (migFuture.isSuccess()) {
                logger.info("[migrateNextCluster][{}] success {}", cluster, migFuture.get().getMsg());
            } else {
                logger.info("[migrateNextCluster][{}] fail", cluster, migFuture.cause());
            }
        });

        return true;
    }

    private CommandFuture<BeaconMigrationResponse> reqMigration(String cluster, String targetDc) {
        CommandFuture<BeaconMigrationResponse> future = new DefaultCommandFuture<>();

        BeaconMigrationRequest request = new BeaconMigrationRequest();
        request.setClusterName(cluster);
        request.setIsForced(true);
        request.setTargetIDC(targetDc);
        request.setGroups(Collections.emptySet());

        logger.info("[reqMigration][{}] at {}", cluster, System.currentTimeMillis());
        client.post()
                .uri(console + "/api/beacon/migration/sync")
                .bodyValue(request)
                .retrieve()
                .bodyToMono(BeaconMigrationResponse.class)
                .subscribe(future::setSuccess, future::setFailure);

        return future;
    }

    @Override
    protected boolean resetDbData() {
        return false;
    }

    @After
    public void afterContinueMigrationTest() {
        scheduled.shutdownNow();
    }

}


text:
var _ = require("underscore");
var fs = require("fs");

function identity() {
  return Array.prototype.slice.call(arguments, 0).shift();
}

exports.directory = function() {
  var dirs = Array.prototype.slice.call(arguments, 0),
  __procs = {},
  me = this;

  this.map = function(k,fn) {
    __procs[k] = fn;
    return this;
  }

  this.load = function() {
    var results = []
    dirs.forEach(function(f) {
      results.push(__process(f));
    });
    return _.extend.apply(_, results);
  }

  this.name = function(name) {
    return (__procs['name'] || identity)(name);
  }

  var __process = function(f) {
    var o = {}
    if(fs.statSync(f).isFile()){
      if(f.split(".").pop() == 'js'){
        o = {}
        o[me.name(f)] = require(f); //only require js files
        return o;
      }
    } else {
      var files = fs.readdirSync(f);
      for (n in files) {
        if(!(files[n][0] === '.')) {
          _.extend(o, __process(f + "/" + files[n])); //collect results in o
        }
      }
      return o;
    }
  }

  return this;
}

exports.connectionString = function(settings) {
  return "mongodb://" + settings.host + ":" + settings.port + "/" + settings.name;
}


text:
// Copyright Dirk Lemstra https://github.com/dlemstra/Magick.NET.
// Licensed under the Apache License, Version 2.0.

using ImageMagick;
using Xunit;

namespace Magick.NET.Tests
{
    public partial class ColorHSLTests
    {
        public class TheProperties
        {
            [Fact]
            public void ShouldSetTheCorrectValue()
            {
                var color = new ColorHSL(0, 0, 0);

                color.Hue = 1;
                Assert.Equal(1, color.Hue);
                Assert.Equal(0, color.Saturation);
                Assert.Equal(0, color.Lightness);

                color.Saturation = 2;
                Assert.Equal(1, color.Hue);
                Assert.Equal(2, color.Saturation);
                Assert.Equal(0, color.Lightness);

                color.Lightness = 3;
                Assert.Equal(1, color.Hue);
                Assert.Equal(2, color.Saturation);
                Assert.Equal(3, color.Lightness);
            }
        }
    }
}


text:
package org.ebayopensource.turmeric.monitoring.client.model;

import com.google.gwt.core.client.GWT;

/**
 * The Class MetricsDataRequest.
 */
public class MetricsDataRequest {

    /**
     * Gets the rest url.
     *
     * @param criteria the criteria
     * @param resourceCriteria the resource criteria
     * @return the rest url
     */
    public static String getRestURL (MetricCriteria criteria, MetricResourceCriteria resourceCriteria) {
        String url = GWT.getModuleBaseURL()+"smqs";
        url += "?"+getUrl(criteria, resourceCriteria);

        return url;
    }

    /**
     * Gets the rest download url.
     *
     * @param criteria the criteria
     * @param resourceCriteria the resource criteria
     * @return the rest download url
     */
    public static String getRestDownloadUrl (MetricCriteria criteria, MetricResourceCriteria resourceCriteria) {
        String url = GWT.getModuleBaseURL()+"dwnld";
        url += "?"+getUrl(criteria, resourceCriteria);
        return url;
    }

    private static String getUrl (MetricCriteria criteria, MetricResourceCriteria resourceCriteria) {
        String url = "";
        url += MetricsQueryService.SERVICE_NAME_HEADER_VALUE;
        url += "&"+MetricsQueryService.OPERATION_NAME_HEADER+"=getMetricsData";
        url += "&"+ConsoleService.USECASE_HEADER_VALUE;
        url += "&"+ConsoleService.NV_DATA_FORMAT_HEADER_VALUE;
        url += "&"+ConsoleService.JSON_RESPONSE_FORMAT_HEADER_VALUE;
        url += "&"+MetricsQueryService.NV_NAMESPACE;
        url+=criteria.asRestUrl();
        url+=resourceCriteria.asRestUrl();

        return url;

    }
}


text:
<!doctype html>

        <meta charset="utf-8">
        <meta http-equiv="x-ua-compatible" content="ie=edge">
        <title>Virtual Exchange</title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
      	<link href="http://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
        <link href='https://fonts.googleapis.com/css?family=Comfortaa:400,700' rel='stylesheet' type='text/css'>
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css">
		<link rel="stylesheet" href="assets/css/dist/main.css">
		<link rel="stylesheet" href="assets/css/dist/seccion.css">

                    menu

Features

Clientes

Nosotros

Contacto

Features

Clientes

Nosotros

Contacto

Contacto

                        <form class="col s12">

                                    account_circle
                                    <input id="icon_prefix" type="text" class="validate">
                                    Nombre

                                    phone
                                    <input id="icon_telephone" type="tel" class="validate">
                                    Teléfono

                                    email
                                    <input id="icon_email" type="email" class="validate">
                                    Email

                                    comment
                                    <textarea id="textarea1" class="materialize-textarea"></textarea>
                                    Tu comentario

                                    Enviar

                        </form>

Virtual Exchange es un producto tecnológico ficticio de realidad aumentada creado por Pablo Valor y Gerardo Ferrari para la materia Visualización e Interfaces. Universidad Nacional de La Matanza.

Virtual Exchange© 2016 | Todos los derechos reservados.

Mapa del sitio

Features

Clientes

Nosotros

Contacto

-  Hecho con  Buenos Aires | Argentina  -


text:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- $This file is distributed under the terms of the license in /doc/license.txt$ -->

<!-- See guidelines in vitro/doc/list_view_configuration_guidelines.txt -->

<!--
subject is Department, property is offersCourse
example:
    'subject': '<http://vivo.brown.edu/individual/org-brown-univ-dept70>',
    'property': '<http://vivo.brown.edu/ontology/vivo-brown/offersres>'
-->

<list-view-config>
    <query-select>
        PREFIX afn: <http://jena.hpl.hp.com/ARQ/function#>
        PREFIX blocal: <http://vivo.brown.edu/ontology/vivo-brown/>
        PREFIX vivo: <http://vivoweb.org/ontology/core#>
        PREFIX owl: <http://www.w3.org/2002/07/owl#>
        PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
        PREFIX vitro: <http://vitro.mannlib.cornell.edu/ns/vitro/0.7#>
        PREFIX event: <http://purl.org/NET/c4dm/event.owl#>
        PREFIX bprofile: <http://vivo.brown.edu/ontology/profile#>

        SELECT DISTINCT ?res ?label ?faculty ?name ?date

        WHERE {
                ?subject ?property ?res .
                ?res rdfs:label ?label .
                ?res ?p ?faculty .
                ?faculty a vivo:FacultyMember .
                ?faculty rdfs:label ?name .
                OPTIONAL {?res bprofile:startDate ?date .}

        } ORDER BY ?name DESC(?date)
    </query-select>

    <query-construct>
        PREFIX vivo: <http://vivoweb.org/ontology/core#>
        PREFIX owl: <http://www.w3.org/2002/07/owl#>
        PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
        PREFIX vitro: <http://vitro.mannlib.cornell.edu/ns/vitro/0.7#>
        PREFIX event: <http://purl.org/NET/c4dm/event.owl#>
        PREFIX blocal: <http://vivo.brown.edu/ontology/vivo-brown/>
        PREFIX bprofile: <http://vivo.brown.edu/ontology/profile#>

        CONSTRUCT {
            ?subject ?property ?res .
            ?res rdfs:label ?label .
            ?res ?p ?faculty .
            ?faculty a vivo:FacultyMember .
            ?faculty rdfs:label ?name .
            ?res bprofile:startDate ?date .
        } WHERE {
            {
                ?subject ?property ?res .
                ?res rdfs:label ?label .
                ?res ?p ?faculty .
                ?faculty a vivo:FacultyMember .
                ?faculty rdfs:label ?name .
            } UNION {
                ?subject ?property ?res .
                ?res bprofile:startDate ?startDate .
                BIND(year(?startDate) as ?date)
            }
        }
    </query-construct>

    <template>propStatement-showFacultyWithDates.ftl</template>
</list-view-config>


text:
<?php
    header("Content-type: application/x-www-form-urlencoded");

    $SID = $_POST['PHPSESSID'];

    session_id($SID);
    session_start();

    if(!isset($_SESSION['user_id'])){
        //need to echo an error
        echo "failed";
    }else {

        require('../../../databaseConnection.php');

        $PID = $_POST['PatientID'];

        if(strcmp($_SESSION['Level'], 'pro') !== 0){
            $StaffID = $_SESSION['SupID'];
        }else{
            $StaffID = $_SESSION['user_id'];
        }

        $query = "select NameLast,NameFirst,DOB
                        FROM Patient
                        WHERE PatientID ='$PID' and CPID = '$StaffID'";

        $queryResult = @mysqli_query($databaseConnection, $query) OR trigger_error($databaseConnection->error . "[ $query]");

        $results = array();

        while ($resultRow = mysqli_fetch_assoc($queryResult)) {
            $results['NameLast'] = $resultRow['NameLast'];
            $results['NameFirst'] = $resultRow['NameFirst'];
            $results['DOB'] = $resultRow['DOB'];
        }
        echo json_encode($results);
    }


text:
<?php
/**
 * Console
 *
 * @author: FangYutao <fangyutao1993@hotmail.com>
 * @since : 2017-08-05
 */

namespace App\Librarys;

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;

abstract class Console extends Application
{
    protected $commands = [];

    public function __construct($name = APP_NAME, $version = APP_VERSION)
    {
        parent::__construct($name, $version);
        $this->initCommands();
    }

    public function run(InputInterface $input = null, OutputInterface $output = null)
    {
        if (null === $input) $input = new ArgvInput();
        if (null === $output) $output = new ConsoleOutput();
        if (!$this->check($input, $output)) exit;
        return parent::run($input, $output);
    }

    /**
     * Check environment
     *
     * @param OutputInterface $output
     * @return mixed
     */
    protected function check(InputInterface $input, OutputInterface $output)
    {
        return true;
    }

    /**
     * Init
     */
    protected function initCommands()
    {
        foreach ($this->commands as $command) {
            $this->add(new $command);
        }
    }
}