API, служащий для отображения состояния синхронизации ресурсов рабочей области с ресурсами, расположенными в другом месте, и для управления этим состоянием, являются новшеством в Eclipse 3.0. В другом месте - это значит, например, вне рабочей области. Синхронизация - это процесс сравнения изменений ресурсов, находящихся в разных местах. При желании пользователь может повлиять на состояние синхронизации, выполнив какое-либо действие. API синхронизации не связан с API класса RepositoryProvider и поэтому может работать без привязки к типу хранилища. Назначение API синхронизации - упростить реализацию различных способов представления состояния синхронизации ресурсов. В сущности, для API необходимы не средства воздействия на состояние синхронизации, а средства для его запроса. Способы воздействия оставим разработчику (хотя UI предоставляет точки для добавления в меню заданных пунктов).
Прежде, чем говорить об API синхронизации, давайте заострим внимание на некоторых терминах и понятиях, необходимых для описания синхронизации рабочей области.
Вариант ресурса: Локальный ресурс, сопоставляемый с ресурсом, находящимся в другом месте. К удаленному ресурсу можно обращаться как к варианту локального. Ресурсы могут быть очень похожими, но при этом незначительно отличаться (либо за счет изменений локального ресурса, либо за счет изменений удаленной копии). Мы принимаем локальный ресурс за эталон и обращаемся к нему как к ресурсу, а к удаленным копиям - как к вариантам этого ресурса.
Синхронизировать: Действие, обозначающее отображение для пользователя различий между вариантами ресурсов. Синхронизация не влияет на состояние вариантов, но дает пользователю представление о различиях между разными наборами вариантов. Однако, обычно пользователю предоставляется возможность изменить состояние варианта (например, восстановить вариант или, наоборот, скопировать его в библиотеку) при синхронизации.
Двухсторонняя и трехсторонняя синхронизация: Существуют два основных метода определения состояния синхронизации: двусторонний и трехсторонний. Двухстороннее сравнение сравнивает локальный ресурс с одним вариантом, считая последний удаленным. При таком типе сравнения можно увидеть только различия между двумя ресурсами, но нельзя проследить взаимосвязь изменений. В большинстве систем хранения кода используется сравнение трех вариантов. При таком типе сравнения вызывается локальный ресурс, вариант удаленного ресурса и вариант базового ресурса. Вариант базового ресурса представляет собой общего предка локального и удаленного ресурсов. Такой алгоритм дает более сложные состояния синхронизации, в которых видно направление изменений.
Таблица 1: Состояния синхронизации
Двухсторонняя Трехсторонняя Изменен
Удален
ДобавленИсходящее изменение
Входящее изменение
Исходящее удаление
Входящее удаление
Исходящее добавление
Входящее добавление
Конфликтующее изменение
Конфликтующее удаление
Конфликтующее добавление
Для описания состояния синхронизации служат классы 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().
Можно долго объяснять, как управлять состоянием синхронизации, но давайте посмотрим, как на практике показать это состояние пользователю. Для отображения состояния синхронизации и предоставления пользователю возможности воздействовать на него служит компонент пользовательского интерфейса ISynchronizeParticipant. Участники синхронизации отображаются в панели Синхронизация, но можно отобразить их в диалогах или мастерах. Это самый основной компонент, реализующий поддержку отображения для пользователей состояний синхронизации любого типа, включая и те, которые не основываются на SyncInfo и Подписчиках.
Добавить мастера создания синхронизации можно с помощью точки расширения org.eclipse.team.ui.synchronizeWizards. Она помещает мастера в глобальное действие синхронизации и в панель Синхронизация, так что пользователи смогут быстро создать синхронизацию нужного типа.
Однако, если реализован Подписчик, то можно воспользоваться конкретным агентом SubscriberParticipant . Его возможности следующие:
Проще всего объяснить это все на примере. Обратитесь к примеру Синхронизация локальной хронологии и посмотрите, как сочетаются все эти возможности. Сведения об использовании более сложных API можно найти в главе Продвигаемся дальше.