A melhor maneira de entender os APIs de Sincronização é criar um exemplo simples que, na verdade, funciona. Neste exemplo, estaremos criando uma página na Visualização de Sincronização que exibirá o estado de histórico local mais recente para todos os arquivos no espaço de trabalho. A sincronização de histórico local será automaticamente atualizada quando as alterações forem feitas no espaço de trabalho e um editor de comparação poderá ser aberto para procurar, mesclar e alterar. Incluiremos também um decorador personalizado para mostrar o último timestamp do elemento de histórico local e uma ação para reverter os arquivos do espaço de trabalho para o estado de histórico local salvo recentemente. Esse é um excelente exemplo porque já temos uma loja de variantes de recurso disponíveis e não temos que gerenciá-la.
Para lembrar deste exemplo, utilizaremos um exemplo em execução. Muitas coisas,
mas não todas, do código fonte serão incluídas nesta página. O código fonte
completo pode ser localizado no pacote de histórico local do plug-in de org.eclipse.team.examples.filesystem.
Você pode registrar a saída do projeto do repositório CVS e utilizá-lo como
referência enquanto está lendo este tutorial. Renúncia: O
código-fonte
no plug-in de exemplo pode ser alterado com o tempo. Para obter uma cópia que corresponda
ao que é utilizado neste exemplo, você pode registrar a saída do projeto utilizando a marcação da versão
3.0 (mais provavelmente R3_0) ou uma marcação de data de 28 de junho de 2004.
Esta captura de tela mostra a sincronização do histórico local na Visualização de
Sincronização. Com isso você pode procurar as alterações entre o recurso local e o estado
mais recente no histórico. Há um decorador personalizado para exibir o timestamp associado
à entrada do histórico local e uma ação personalizada para reverter seu arquivo
para o conteúdo no histórico local. Observe também que a apresentação Visualização
de Sincronização padrão é utilizada e fornece anotações de problemas, layout de pasta
compactada e botões de navegação.
A primeira etapa é definir uma variante para representar os elementos do histórico local. Isso permitirá que as APIs de sincronização acessem o conteúdo do histórico local para que possa ser comparado com o conteúdo atual e exibido para o usuário.
public class LocalHistoryVariant implements IResourceVariant {
private final IFileState state;
public LocalHistoryVariant(IFileState state) {
this.state = state;
}
public String getName() {
return state.getName();
}
public boolean isContainer() {
return false;
}
public IStorage getStorage(IProgressMonitor monitor) throws TeamException {
return state;
}
public String getContentIdentifier() {
return Long.toString(state.getModificationTime());
}
public byte[] asBytes() {
return null;
}
}
Já que a interface IFileState já fornece acesso ao conteúdo do arquivo a partir do histórico
local (por exemplo, implementa a interface IStorage), isso foi fácil.
Normalmente, ao criar uma variante, você tem que fornecer uma maneira de acessar o
conteúdo, um identificador de conteúdo que será exibido para o usuário para identificar
essa variante e um nome. O método asBytes() é requerido somente se persistir a variante
entre as sessões.
A seguir, vamos criar um comparador de variantes que permite o cálculo de SyncInfo
para comparar recursos locais com suas variantes. Isso é fácil porque a existência
de um estado de histórico local indica que o conteúdo do estado de histórico local
difere do conteúdo atual do arquivo. Isso ocorre porque a especificação
para o histórico local diz que não criará um estado de histórico local
se o arquivo não foi alterado.
public class LocalHistoryVariantComparator implements IResourceVariantComparator {
public boolean compare(IResource local, IResourceVariant remote) {
return false;
}
public boolean compare(IResourceVariant base, IResourceVariant remote) {
return false;
}
public boolean isThreeWay() {
return false;
}
}
Podemos simplesmente retornar false ao comparar o arquivo com o estado de histórico local porque sabemos que a existência do estado de histórico local significa que é diferente do local. Além disso, a sincronização com o histórico local é somente de duas mãos porque não temos acesso a um recurso base para que o método para comparar duas variantes de recurso não seja utilizado.
Observe que o cálculo de sincronização não chamará o método compare do comparador se a variante não existir (por exemplo, é nulo). É chamado somente se ambos os elementos existirem. No nosso exemplo, isso ocorreria para os arquivos que não possuem um histórico local e para todas as pastas (que nunca possuem um histórico local). Para lidar com isso, precisamos definir nossa própria subclasse de SyncInfo para modificar o estado de sincronização calculado para esses casos.
public class LocalHistorySyncInfo extends SyncInfo {
public LocalHistorySyncInfo(IResource local, IResourceVariant remote, IResourceVariantComparator comparator) {
super(local, null, remote, comparator);
}
protected int calculateKind() throws TeamException {
if (getRemote() == null)
return IN_SYNC;
else
return super.calculateKind();
}
}
Substituímos o construtor para sempre fornecer uma base que seja nula (já que estamos utilizando somente a comparação de duas mãos) e modificamos o cálculo do tipo de sincronização para retornar IN_SYNC se não houver remoto (já que nos preocupamos somente com os casos em que há um arquivo local e um estado de arquivo no histórico local.
Agora criaremos um Assinante que fornecerá acesso às variantes de recurso no histórico local. Já que o histórico local pode ser salvo para qualquer arquivo no espaço de trabalho, o Assinante do histórico local supervisionará todos os recursos e o conjunto de raízes serão todos os projetos no espaço de trabalho. Além disso, não há necessidade de fornecer a habilidade de atualizar o assinante desde que o histórico local seja alterado somente quando o conteúdo de um arquivo local for alterado. Portanto, podemos atualizar nosso estado sempre que um delta de recursos ocorrer. Isso deixa apenas dois métodos interessantes no nosso assinante de histórico local: obtendo uma SyncInfo e atravessando o espaço de trabalho.
public SyncInfo getSyncInfo(IResource resource) throws TeamException {
try { IResourceVariant variant = null;
if(resource.getType() == IResource.FILE) {
IFile file = (IFile)resource;
IFileState[] states = file.getHistory(null);
if(states.length > 0) {
// last state only
variant = new LocalHistoryVariant(states[0]);
}
}
SyncInfo info = new LocalHistorySyncInfo(resource, variant, comparator);
info.init();
return info;
} catch (CoreException e) {
throw TeamException.asTeamException(e);
}
}
O Assinante retornará uma nova instância de SyncInfo que irá conter o estado mais recente do arquivo no histórico local. O SyncInfo é criado com uma variante de histórico local para o elemento remoto. Para os projetos, as pastas e os arquivos sem histórico local, nenhuma variante de recurso remoto é fornecida, que resultará no recurso sendo considerado em sincronização por causa do método calculateKind no nosso LocalHistorySyncInfo.
O código remanescente no assinante do histórico local é a implementação do método members:
public IResource[] members(IResource resource) throws TeamException {
try { if(resource.getType() == IResource.FILE)
return new IResource[0];
IContainer container = (IContainer)resource;
List existingChildren = new ArrayList(Arrays.asList(container.members()));
existingChildren.addAll(
Arrays.asList(container.findDeletedMembersWithHistory(IResource.DEPTH_INFINITE, null)));
return (IResource[]) existingChildren.toArray(new IResource[existingChildren.size()]);
} catch (CoreException e) {
throw TeamException.asTeamException(e);
}
}
O detalhe interessante deste método é que ele retornará filhos não existentes se um recurso excluído tiver um histórico local. Isso permitirá que nosso Assinante retorne SyncInfo para elementos que existem somente no histórico local e não estão mais no espaço de trabalho.
Até agora criamos as classes que fornecem acesso ao SyncInfo para os elementos no histórico local. A seguir, criaremos os elementos da UI que nos permitirão ter uma página na Visualização de Sincronização para exibir o último estado do histórico para cada elemento no histórico local. Já que temos um Assinante, incluir isso na Visualização de Sincronização é fácil. Vamos começar incluindo um ponto de extensão do participante da sincronização:
<extension point="org.eclipse.team.ui.synchronizeParticipants">
<participant
persistent="false"
icon="synced.png"
class="org.eclipse.team.synchronize.example.LocalHistoryParticipant"
name="Latest From Local History"
id="org.eclipse.team.synchronize.example"/>
</extension>
A seguir, temos que implementar o LocalHistoryParticipant. Ele colocará SubscriberParticipant em uma subclasse, que fornecerá todo o comportamento padrão para coletar SyncInfo do assinante e atualizar estados de sincronização quando as alterações do espaço de trabalho ocorrerem. Além disso, incluiremos uma ação para reverter os recursos do espaço de trabalho para o mais recente no histórico local.
Primeiramente, verificaremos como uma ação personalizada é incluída no participante
public static final String CONTEXT_MENU_CONTRIBUTION_GROUP = "context_group_1"; //$NON-NLS-1$
private class LocalHistoryActionContribution extends SynchronizePageActionGroup {
public void initialize(ISynchronizePageConfiguration configuration) {
super.initialize(configuration);
appendToGroup(
ISynchronizePageConfiguration.P_CONTEXT_MENU, CONTEXT_MENU_CONTRIBUTION_GROUP,
new SynchronizeModelAction("Revert to latest in local history", configuration) { //$NON-NLS-1$
protected SynchronizeModelOperation getSubscriberOperation(ISynchronizePageConfiguration configuration, IDiffElement[] elements) {
return new RevertAllOperation(configuration, elements);
}
});
}
}
Aqui, estamos incluindo uma SynchronizeMoidelAction específica e uma operação. O comportamento que obtemos gratuitamente aqui é a habilidade de execução no segundo plano e mostrar o status de ocupado para os nós que estão sendo trabalhados. A ação reverte todos os recursos no espaço de trabalho para o estado mais recente no histórico local. A ação é incluída na inclusão de uma contribuição de ação na configuração dos participantes. A configuração é utilizada para descrever as propriedades utilizadas para construir a página do participante que exibirá a UI de sincronização verdadeira.
O participante inicializará a configuração conforme a seguir para incluir o grupo de ação do histórico local para o menu de contexto:
protected void initializeConfiguration(ISynchronizePageConfiguration configuration) {
super.initializeConfiguration(configuration);
configuration.addMenuGroup(
ISynchronizePageConfiguration.P_CONTEXT_MENU,
CONTEXT_MENU_CONTRIBUTION_GROUP);
configuration.addActionContribution(new LocalHistoryActionContribution()); configuration.addLabelDecorator(new LocalHistoryDecorator());
}
Agora vamos verificar como podemos fornecer uma decoração personalizada. A última linha do método acima registra o seguinte decorador com a configuração de página.
public class LocalHistoryDecorator extends LabelProvider implements ILabelDecorator {
public String decorateText(String text, Object element) {
if(element instanceof ISynchronizeModelElement) {
ISynchronizeModelElement node = (ISynchronizeModelElement)element;
if(node instanceof IAdaptable) {
SyncInfo info = (SyncInfo)((IAdaptable)node).getAdapter(SyncInfo.class);
if(info != null) {
LocalHistoryVariant state = (LocalHistoryVariant)info.getRemote();
return text+ " ("+ state.getContentIdentifier() + ")";
}
}
}
return text;
}
public Image decorateImage(Image image, Object element) {
return null;
}
}
O decorador extrai o recurso do elemento do modelo que aparece na visualização de sincronização e anexa o identificador de conteúdo da variante de histórico local à etiqueta de texto que aparece na visualização.
A última parte é fornecer um assistente que criará o participante do histórico local. A perspectiva da Sincronização da Equipe define uma ação de sincronização global que permite que os usuários criem rapidamente uma sincronização. Além disso, a habilidade de criar sincronizações disponíveis a partir da barra de ferramentas de visualização Sincronizar. Para começar, crie um ponto de extensão synchronizeWizards:
<extension
point="org.eclipse.team.ui.synchronizeWizards">
<assistente
class="org.eclipse.team.synchronize.example.LocalHistorySynchronizeWizard"
icon="synced.png"
description="Creates a synchronization against the latest local history state of all resources in the workspace"
name="Latest From Local History Synchronize"
id="ExampleSynchronizeSupport.wizard1"/>
</extension>
Isso incluirá nosso assistente na lista e nos assistentes do método finish(), simplesmente criaremos nosso participante e o incluiremos no gerenciador de sincronização.
LocalHistoryPartipant participant = new LocalHistoryPartipant();
ISynchronizeManager manager = TeamUI.getSynchronizeManager();
manager.addSynchronizeParticipants(new ISynchronizeParticipant[] {participant});
ISynchronizeView view = manager.showSynchronizeViewInActivePage();
view.display(participant);
Este é um exemplo simples de como utilizar as APIs de sincronização e observamos
alguns detalhes para facilitar o entendimento do exemplo.
Escrever um suporte de sincronização responsivo e preciso não é essencial, a parte mais
difícil é o gerenciamento das informações de sincronização e a notificação das alterações
de estado da sincronização. A interface com o usuário, se a que estiver associada a
SubscriberParticipants for adequada, é a parte fácil, uma vez que a implementação
do Assinante esteja concluída. Para obter mais exemplos, consulte o
plug-in de org.eclipse.team.example.filesystem
e procure as subclasses no espaço de trabalho do Assinante e
ISynchronizeParticipant.
A próxima seção descreve algumas classes e interfaces que podem ajudar a gravar um
Assinante do ponto de partida, incluindo como armazenar os estados de sincronização em cache
entre as sessões do workbench.