Поддержка синхронизации

API, служащий для отображения состояния синхронизации ресурсов рабочей области с ресурсами, расположенными в другом месте, и для управления этим состоянием, являются новшеством в Eclipse 3.0. В другом месте - это значит, например, вне рабочей области. Синхронизация - это процесс сравнения изменений ресурсов, находящихся в разных местах. При желании пользователь может повлиять на состояние синхронизации, выполнив какое-либо действие. API синхронизации не связан с API класса RepositoryProvider и поэтому может работать без привязки к типу хранилища. Назначение API синхронизации - упростить реализацию различных способов представления состояния синхронизации ресурсов. В сущности, для API необходимы не средства воздействия на состояние синхронизации, а средства для его запроса. Способы воздействия оставим разработчику (хотя UI предоставляет точки для добавления в меню заданных пунктов).

Терминология

Прежде, чем говорить об API синхронизации, давайте заострим внимание на некоторых терминах и понятиях, необходимых для описания синхронизации рабочей области.

Вариант ресурса: Локальный ресурс, сопоставляемый с ресурсом, находящимся в другом месте. К удаленному ресурсу можно обращаться как к варианту локального. Ресурсы могут быть очень похожими, но при этом незначительно отличаться (либо за счет изменений локального ресурса, либо за счет изменений удаленной копии). Мы принимаем локальный ресурс за эталон и обращаемся к нему как к ресурсу, а к удаленным копиям - как к вариантам этого ресурса.

Синхронизировать: Действие, обозначающее отображение для пользователя различий между вариантами ресурсов. Синхронизация не влияет на состояние вариантов, но дает пользователю представление о различиях между разными наборами вариантов. Однако, обычно пользователю предоставляется возможность изменить состояние варианта (например, восстановить вариант или, наоборот, скопировать его в библиотеку) при синхронизации.

Двухсторонняя и трехсторонняя синхронизация: Существуют два основных метода определения состояния синхронизации: двусторонний и трехсторонний. Двухстороннее сравнение сравнивает локальный ресурс с одним вариантом, считая последний удаленным. При таком типе сравнения можно увидеть только различия между двумя ресурсами, но нельзя проследить взаимосвязь изменений. В большинстве систем хранения кода используется сравнение трех вариантов. При таком типе сравнения вызывается локальный ресурс, вариант удаленного ресурса и вариант базового ресурса. Вариант базового ресурса представляет собой общего предка локального и удаленного ресурсов. Такой алгоритм дает более сложные состояния синхронизации, в которых видно направление изменений.

Таблица 1: Состояния синхронизации

Двухсторонняя Трехсторонняя
Изменен
Удален
Добавлен
Исходящее изменение
Входящее изменение
Исходящее удаление
Входящее удаление
Исходящее добавление
Входящее добавление
Конфликтующее изменение
Конфликтующее удаление
Конфликтующее добавление

Основные сведения - SyncInfo

Для описания состояния синхронизации служат классы org.eclipse.team.core.synchronize. Самым важным является класс SyncInfo, поскольку он отражает реальное состояние синхронизации. Этот класс применяется так:

SyncInfo info = getSyncInfo(resource); // модель метода получения состояния синхронизации ресурса
int changekind = info.getKind();
if(info.getResourceComparator().isThreeWay()) {
if((changeKind & SyncInfo.DIRECTION_MASK) == SyncInfo.INCOMING) {
// какие-либо действия
}
} else if(changeKind == SyncInfo.CHANGE) {
// действия по ветке else
}

Класс SyncInfo реализует и двухсторонний, и трехсторонний алгоритмы сравнения, от клиента требуются ресурсы и класс для их сравнения (IResourceVariantComparator). Пример кода для сравнения вариантов:

public class TimestampVariantComparator implements IResourceVariantComparator {	
protected boolean compare(IResourceVariant e1, IResourceVariant e2) {
if(e1.isContainer()) {
if(e2.isContainer()) {
return true;
}
return false;
}
if(e1 instanceof MyResourceVariant && e2 instanceof MyResourceVariant) {
MyResourceVariant myE1 = (MyResourceVariant)e1;
MyResourceVariant myE2 = (MyResourceVariant)e2;
return myE1.getTimestamp().equals(myE2.getTimestamp());
}
return false;
}
protected boolean compare(IResource e1, IResourceVariant e2) {

}
public boolean isThreeWay() {
return true;
}
}

SyncInfo info = new SyncInfo(resource, variant1, variant2, new TimestampComparator());
info.init(); // расчет sync info

Кроме того, в этом пакете предусмотрены коллекции, предназначенные для хранения SyncInfo и фильтров для экземпляров SyncInfo.

Управление состоянием синхронизации

Из приведенных выше примеров можно увидеть, что классы SyncInfo и IResourceVariantComparator предоставляют доступ к состоянию синхронизации ресурсов. Но пока еще непонятно, как этим состоянием можно управлять. Доступ к состоянию синхронизации ресурсов локальной рабочей области с наборами вариантов для этих ресурсов предоставляет класс Subscriber. При этом может использоваться либо двухсторонний, либо трехсторонний метод сравнения, в зависимости от рода подписчика. Подписчик предоставляет следующие возможности:

API не определяют, как был создан Подписчик. Это уже дело конкретной реализации. Например, модуль CVS создает одного Подписчика при выполнении слияния, другого - для сравнения, а третьего - при синхронизации локальной рабочей области с текущей ветвью.

Теперь давайте вернемся к первому примеру и посмотрим, как можно получить доступ к SyncInfo с помощью Подписчика.

// Создаем подписчика файловой системы и указываем, что
// он будет синхронизироваться с заданным участком файловой системы
Subscriber subscriber = new FileSystemSubscriber("c:\temp\repo");

// Разрешаем Подписчику обновить свое состояние
subscriber.refresh(subscriber.roots(), IResource.DEPTH_INFINITE, monitor);

// Собираем состояния синхронизации и выводим их на печать
IResource[] children = subscriber.roots();
for(int i=0; i < children.length; i++) {
printSyncState(children[i]);
}

...

void printSyncState(Subscriber subscriber, IResource resource) {
System.out.println(subscriber.getSyncInfo(resource).toString());
IResource[] children = subscriber.members(resource);
for(int i=0; i < children.length; i++) {
IResource child = children[i];
if(! child.exists()) {
System.out.println(resource.getFullPath() + " не существует в рабочей области");
}
printSyncState(subscriber, children[i]);
}
}

Важно помнить, что Подписчик знает о ресурсах, не существующих в файловой системе. Несуществующие ресурсы можно получить с помощью методов Subscriber#members() и SyncInfo#getLocal().

Отображение состояния синхронизации в UI

Можно долго объяснять, как управлять состоянием синхронизации, но давайте посмотрим, как на практике показать это состояние пользователю. Для отображения состояния синхронизации и предоставления пользователю возможности воздействовать на него служит компонент пользовательского интерфейса ISynchronizeParticipant. Участники синхронизации отображаются в панели Синхронизация, но можно отобразить их в диалогах или мастерах. Это самый основной компонент, реализующий поддержку отображения для пользователей состояний синхронизации любого типа, включая и те, которые не основываются на SyncInfo и Подписчиках.

Добавить мастера создания синхронизации можно с помощью точки расширения org.eclipse.team.ui.synchronizeWizards. Она помещает мастера в глобальное действие синхронизации и в панель Синхронизация, так что пользователи смогут быстро создать синхронизацию нужного типа.

Однако, если реализован Подписчик, то можно воспользоваться конкретным агентом SubscriberParticipant . Его возможности следующие:

Проще всего объяснить это все на примере. Обратитесь к примеру Синхронизация локальной хронологии и посмотрите, как сочетаются все эти возможности. Сведения об использовании более сложных API можно найти в главе Продвигаемся дальше.