Java tutorial
/******************************************************************************* * Copyright (c) 2013 Instituto Superior Tcnico - Joo Antunes * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html * * Contributors: * Luis Silva - ACGHSync * Joo Antunes - initial API and implementation ******************************************************************************/ package pt.ist.maidSyncher.domain.github; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.apache.commons.lang.ObjectUtils; import org.eclipse.egit.github.core.IRepositoryIdProvider; import org.eclipse.egit.github.core.Repository; import org.eclipse.egit.github.core.User; import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pt.ist.maidSyncher.api.activeCollab.ACCategory; import pt.ist.maidSyncher.api.activeCollab.ACProject; import pt.ist.maidSyncher.domain.MaidRoot; import pt.ist.maidSyncher.domain.SynchableObject; import pt.ist.maidSyncher.domain.activeCollab.ACInstance; import pt.ist.maidSyncher.domain.activeCollab.ACTaskCategory; import pt.ist.maidSyncher.domain.activeCollab.exceptions.TaskNotVisibleException; import pt.ist.maidSyncher.domain.dsi.DSIObject; import pt.ist.maidSyncher.domain.dsi.DSIProject; import pt.ist.maidSyncher.domain.dsi.DSIRepository; import pt.ist.maidSyncher.domain.exceptions.SyncActionError; import pt.ist.maidSyncher.domain.exceptions.SyncEventIncogruenceBetweenOriginAndDestination; import pt.ist.maidSyncher.domain.sync.EmptySyncActionWrapper; import pt.ist.maidSyncher.domain.sync.SyncActionWrapper; import pt.ist.maidSyncher.domain.sync.SyncEvent; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import com.google.common.collect.Iterables; public class GHRepository extends GHRepository_Base implements IRepositoryIdProvider { public GHRepository() { super(); MaidRoot.getInstance().addGhRepositories(this); //the organization of this repository should always be the implicit one setOrganization(MaidRoot.getInstance().getGhOrganization()); } private static final Logger LOGGER = LoggerFactory.getLogger(GHRepository.class); //PropertyDescriptor s names: protected static final String DSC_OWNER = "owner"; protected static final String DSC_HAS_DOWNLOADS = "hasDownloads"; protected static final String DSC_GITURL = "gitUrl"; protected static final String DSC_HAS_ISSUES = "hasIssues"; protected static final String DSC_MASTER_BRANCH = "masterBranch"; protected static final String DSC_DESCRIPTION = "description"; protected static final String DSC_HAS_WIKI = "hasWiki"; protected static final String DSC_PUSHED_AT = "pushedAt"; protected static final String DSC_UPDATED_AT = "updatedAt"; protected static final String DSC_NAME = "name"; protected static final String DSC_OPEN_ISSUES = "openIssues"; protected static final String DSC_SSH_URL = "sshUrl"; protected static final String DSC_SVN_URL = "svnUrl"; protected static final String DSC_CLONE_URL = "cloneUrl"; protected static final String DSC_HTML_URL = "htmlUrl"; @Override public Collection<String> copyPropertiesFrom(Object orig) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, TaskNotVisibleException { Set<String> changedPropertyDescriptors = new HashSet(); changedPropertyDescriptors.addAll(super.copyPropertiesFrom(orig)); //let's take care of the Owner Repository repository = (Repository) orig; User owner = repository.getOwner(); if (owner.getType().equals(User.TYPE_USER)) { GHUser ghUser = GHUser.process(owner); GHUser oldOwner = getOwner(); setOwner(ghUser); if (ObjectUtils.equals(ghUser, oldOwner) == false) changedPropertyDescriptors.add(getPropertyDescriptorNameAndCheckItExists(orig, DSC_OWNER)); } else { GHOrganization ghOrg = GHOrganization.process(owner); GHUser oldOwner = getOwner(); setOwner(ghOrg); if (ObjectUtils.equals(ghOrg, oldOwner) == false) changedPropertyDescriptors.add(getPropertyDescriptorNameAndCheckItExists(orig, DSC_OWNER)); } return changedPropertyDescriptors; } public static GHRepository process(Repository repository, boolean skipSync) { checkNotNull(repository); MaidRoot maidRoot = MaidRoot.getInstance(); return (GHRepository) findOrCreateAndProccess(repository, GHRepository.class, maidRoot.getGhRepositoriesSet(), skipSync); } public static GHRepository process(Repository repository) { return process(repository, false); } public static Set<GHRepository> getRepositoriesWithProjectAssociated() { Set<GHRepository> repositories = MaidRoot.getInstance().getGhRepositoriesSet(); return new HashSet(Collections2.filter(repositories, new Predicate<GHRepository>() { @Override public boolean apply(GHRepository input) { if (input == null) return false; return input.getDSIObject() != null && ((DSIRepository) input.getDSIObject()).getProject() != null; } })); } public static Set<GHRepository> getRepositorysWithProjects() { Set<GHRepository> repositories = MaidRoot.getInstance().getGhRepositoriesSet(); return new HashSet(Collections2.filter(repositories, new Predicate<GHRepository>() { @Override public boolean apply(GHRepository input) { if (input == null) return false; return input.getDSIObject() != null && ((DSIRepository) input.getDSIObject()).getProject() != null; } })); } public static Set<GHRepository> getActiveRepositories() { Set<GHRepository> repositories = MaidRoot.getInstance().getGhRepositoriesSet(); return new HashSet(Collections2.filter(repositories, new Predicate<GHRepository>() { @Override public boolean apply(GHRepository input) { if (input == null) return false; return true; } })); } @Override public DSIObject getDSIObject() { return super.getDsiObjectRepository(); } @Override public DSIObject findOrCreateDSIObject() { DSIObject dsiObject = getDSIObject(); if (dsiObject == null) { dsiObject = new DSIRepository();//let's take care of the //default project and the task category on the sync setDsiObjectRepository((DSIRepository) dsiObject); } return dsiObject; } @Override public DateTime getUpdatedAtDate() { return getUpdatedAt() == null ? getCreatedAt() : getUpdatedAt(); } @Override public SyncActionWrapper sync(SyncEvent syncEvent) { //every GHRepository should have a default ACProject //and a ACTaskCategory. SyncActionWrapper syncActionWrapperToReturn = null; if (syncEvent.getTypeOfChangeEvent().equals(SyncEvent.TypeOfChangeEvent.CREATE)) { //then we should need to create an ACProject (if it doesn't exist) //and/or a ACTaskCategory (also if it doesn't exist) //validate the SyncUniverse if (syncEvent.getTargetSyncUniverse().equals(SyncEvent.SyncUniverse.GITHUB)) throw new SyncEventIncogruenceBetweenOriginAndDestination("For syncEvent: " + syncEvent.toString()); syncActionWrapperToReturn = syncCreateEvent(syncEvent); } else if (syncEvent.getTypeOfChangeEvent().equals(SyncEvent.TypeOfChangeEvent.UPDATE)) { //let's retrieve the default project, and the task category: DSIRepository dsiRepository = (DSIRepository) getDSIObject(); Collection<ACTaskCategory> acTaskCategories = dsiRepository.getAcTaskCategoriesSet(); pt.ist.maidSyncher.domain.activeCollab.ACProject defaultACProject = dsiRepository.getDefaultProject(); syncActionWrapperToReturn = syncUpdateEvent(defaultACProject, acTaskCategories, syncEvent); } else { LOGGER.warn("Read and Delete events not supported yet. " + syncEvent); syncActionWrapperToReturn = new EmptySyncActionWrapper(syncEvent); } return syncActionWrapperToReturn; } private SyncActionWrapper syncUpdateEvent(pt.ist.maidSyncher.domain.activeCollab.ACProject defaultACProject, Collection<ACTaskCategory> acTaskCategories, final SyncEvent syncEvent) { //both (project and task category) ought to exist, //let's be conservative and throw an error if they don't checkNotNull(defaultACProject); checkNotNull(acTaskCategories); ACProject acProjectAux = null; final DSIRepository dsiRepository = (DSIRepository) getDSIObject(); final Set<ACCategory> acCategoriesToEdit = new HashSet<ACCategory>(); final Set<String> tickedDescriptors = new HashSet<>(); for (String changedDescriptor : syncEvent.getChangedPropertyDescriptorNames().getUnmodifiableList()) { tickedDescriptors.add(changedDescriptor); switch (changedDescriptor) { case DSC_ID: case DSC_OWNER: case DSC_HAS_DOWNLOADS: case DSC_GITURL: case DSC_URL: case DSC_HAS_ISSUES: case DSC_MASTER_BRANCH: case DSC_HAS_WIKI: case DSC_PUSHED_AT: case DSC_UPDATED_AT: case DSC_OPEN_ISSUES: case DSC_SSH_URL: case DSC_SVN_URL: case DSC_CLONE_URL: case DSC_CREATED_AT: case DSC_HTML_URL: case DSC_DESCRIPTION: // the ones that we don't have to do anything // //then we need to do something with the default project's description NOT! // if (acProjectAux == null) { // acProjectAux = new ACProject(); // } // acProjectAux = preFillProjectIfNeeded(defaultACProject, acProjectAux); // acProjectAux.set break; case DSC_NAME: //then we must change the name of the default project, and of all of the acTaskCategories acProjectAux = preFillProjectIfNeeded(defaultACProject, acProjectAux); acProjectAux.setName(getName()); //let's take care of the categories for (ACTaskCategory acTaskCategory : dsiRepository.getAcTaskCategoriesSet()) { acCategoriesToEdit.add(new ACCategory(acTaskCategory.getId(), acTaskCategory.getProject().getId(), ACTaskCategory.REPOSITORY_PREFIX + getName())); } break; default: tickedDescriptors.remove(changedDescriptor); //if we did not fall on any of the above //cases, let's remove it from the ticked descriptors } } final ACProject acProjectToEdit = acProjectAux; return new SyncActionWrapper<SynchableObject>() { @Override public Set<SynchableObject> sync() throws SyncActionError { Set<SynchableObject> changedObjects = new HashSet<SynchableObject>(); Set<ACTaskCategory> newACTaskCategories = new HashSet<>(); try { //let's take care of: //. the project if we need to; if (acProjectToEdit != null) { ACProject newlyCreatedACProject = acProjectToEdit.update(); pt.ist.maidSyncher.domain.activeCollab.ACProject newACProject = pt.ist.maidSyncher.domain.activeCollab.ACProject .process(newlyCreatedACProject, true); dsiRepository.setDefaultProject(newACProject); changedObjects.add(newACProject); } //. the categories if we need to; if (acCategoriesToEdit.isEmpty() == false) { for (ACCategory acCategoryToEdit : acCategoriesToEdit) { ACCategory newACCategory = acCategoryToEdit.update(); ACTaskCategory newACTaskCategory = ACTaskCategory.process(newACCategory, newACCategory.getProjectId(), true); newACTaskCategories.add(newACTaskCategory); changedObjects.add(newACTaskCategory); } //let's set the new task categories //remove the old ones for (ACTaskCategory oldAcTaskCategory : dsiRepository.getAcTaskCategoriesSet()) { dsiRepository.removeAcTaskCategories(oldAcTaskCategory); } for (ACTaskCategory acTaskCategory : newACTaskCategories) { dsiRepository.addAcTaskCategories(acTaskCategory); } } } catch (IOException ex) { throw new SyncActionError(ex, changedObjects); } return changedObjects; } @Override public SyncEvent getOriginatingSyncEvent() { return syncEvent; } @Override public Collection<DSIObject> getSyncDependedDSIObjects() { return Collections.singleton((DSIObject) dsiRepository.getProject()); } @Override public Set<Class> getSyncDependedTypesOfDSIObjects() { return Collections.singleton((Class) DSIProject.class); } @Override public Collection<String> getPropertyDescriptorNamesTicked() { return tickedDescriptors; } }; } private ACProject preFillProjectIfNeeded(pt.ist.maidSyncher.domain.activeCollab.ACProject existingACProject, ACProject acApiProject) { ACProject acProjectToReturn; if (acApiProject != null) { acProjectToReturn = acApiProject; } else { acProjectToReturn = new ACProject(); } try { existingACProject.copyPropertiesTo(acProjectToReturn); } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | TaskNotVisibleException e) { LOGGER.warn("Trying to 'copyPropertiesTo' between domain.ACProject and api.ACProject", e); throw new Error("Trying to 'copyPropertiesTo' between domain.ACProject and api.ACProject", e); } return acProjectToReturn; } // owner hasDownloads gitUrl hasIssues masterBranch description hasWiki pushedAt updatedAt name openIssues sshUrl svnUrl cloneUrl createdAt htmlUrl // private SyncActionWrapper syncCreateEvent(final pt.ist.maidSyncher.domain.activeCollab.ACProject acExistingProject, // final Collection<ACTaskCategory> existingACTaskCategories, final SyncEvent syncEvent) { private SyncActionWrapper syncCreateEvent(final SyncEvent syncEvent) { final Set<String> tickedDescriptors = new HashSet<>(); for (String changedDescriptor : syncEvent.getChangedPropertyDescriptorNames().getUnmodifiableList()) { tickedDescriptors.add(changedDescriptor); switch (changedDescriptor) { case DSC_ID: case DSC_OWNER: case DSC_HAS_DOWNLOADS: case DSC_URL: case DSC_GITURL: case DSC_HAS_ISSUES: case DSC_MASTER_BRANCH: case DSC_HAS_WIKI: case DSC_PUSHED_AT: case DSC_UPDATED_AT: case DSC_OPEN_ISSUES: case DSC_SSH_URL: case DSC_SVN_URL: case DSC_CLONE_URL: case DSC_CREATED_AT: case DSC_HTML_URL: //the ones that we don't have to do anything case DSC_DESCRIPTION: break; case DSC_NAME: break; default: tickedDescriptors.remove(changedDescriptor); //if we did not fall on any of the above //cases, let's remove it from the ticked descriptors } } return new SyncActionWrapper<SynchableObject>() { @Override public Set<SynchableObject> sync() throws SyncActionError { Set<ACTaskCategory> acTaskCategoriesToAssign = new HashSet<>(); Set<SynchableObject> changedObjects = new HashSet<>(); //let's try to see if an ACProject already exists or not pt.ist.maidSyncher.domain.activeCollab.ACProject acExistingProject = pt.ist.maidSyncher.domain.activeCollab.ACProject .findByName(getName()); //let's assert if we need to create the project pt.ist.maidSyncher.domain.activeCollab.ACProject acProjectToReturn = null; if (acExistingProject == null) { //let us create it ACProject newAcProject = new ACProject(); newAcProject.setName(getName()); ACInstance acInstance = MaidRoot.getInstance().getAcInstance(); newAcProject.setCompanyId((int) acInstance.getId()); //lets ignore the leadId, status, budget, and labels try { acProjectToReturn = pt.ist.maidSyncher.domain.activeCollab.ACProject .process(ACProject.create(newAcProject), true); changedObjects.add(acProjectToReturn); } catch (IOException exception) { throw new SyncActionError(exception, changedObjects); } //we must assign it the other TaskCategories from the other GHRepositories for (GHRepository existingGhRepository : GHRepository.getActiveRepositories()) { createNewACTaskCategoryHelper( ACTaskCategory.REPOSITORY_PREFIX + existingGhRepository.getName(), acProjectToReturn.getId(), acTaskCategoriesToAssign, changedObjects); } } //let's gather all of the active ACProjects final Set<pt.ist.maidSyncher.domain.activeCollab.ACProject> activeProjects = pt.ist.maidSyncher.domain.activeCollab.ACProject .getActiveProjects(); //and for each, we should have an ACTaskCategory created or assigned DSIRepository dsiRepository = (DSIRepository) getDSIObject(); dsiRepository.setDefaultProject(acProjectToReturn); //let us assert which ACTaskCategory we have to create //and which ones we should just simply apply Set<pt.ist.maidSyncher.domain.activeCollab.ACProject> projectsToCreateTaskCategoriesFor = new HashSet<>( activeProjects); Collection<ACTaskCategory> existingACTaskCategories = ACTaskCategory .getByName(ACTaskCategory.REPOSITORY_PREFIX + getName()); for (ACTaskCategory existingCategory : existingACTaskCategories) { if (existingCategory.getProject().getArchived() == false) { projectsToCreateTaskCategoriesFor.remove(existingCategory.getProject()); acTaskCategoriesToAssign.add(existingCategory); } } //now, let us create the rest of the categories for (pt.ist.maidSyncher.domain.activeCollab.ACProject acProjectToCreateTCategoryFor : projectsToCreateTaskCategoriesFor) { //let's create it createNewACTaskCategoryHelper(ACTaskCategory.REPOSITORY_PREFIX + getName(), acProjectToCreateTCategoryFor.getId(), acTaskCategoriesToAssign, changedObjects); } //now, let's remove the old ones, and add the new ones dsiRepository.getAcTaskCategoriesSet().clear(); dsiRepository.getAcTaskCategoriesSet().addAll(acTaskCategoriesToAssign); ArrayList<SynchableObject> synchedObjects = new ArrayList<SynchableObject>(); synchedObjects.add(acProjectToReturn); synchedObjects.addAll(dsiRepository.getAcTaskCategoriesSet()); return changedObjects; } @Override public SyncEvent getOriginatingSyncEvent() { return syncEvent; } @Override public Collection<DSIObject> getSyncDependedDSIObjects() { return Collections.emptyList(); } @Override public Set<Class> getSyncDependedTypesOfDSIObjects() { return Collections.emptySet(); } @Override public Collection<String> getPropertyDescriptorNamesTicked() { return tickedDescriptors; } }; } static void createNewACTaskCategoryHelper(String categoryName, long projectId, Set<ACTaskCategory> acTaskCategoriesToAssign, Set<SynchableObject> changedObjects) { checkNotNull(categoryName); checkNotNull(acTaskCategoriesToAssign); checkNotNull(changedObjects); ACCategory acNewCategory = new ACCategory(); acNewCategory.setName(categoryName); try { acNewCategory = ACCategory.create(acNewCategory, projectId, ACTaskCategory.class); } catch (IOException e) { throw new SyncActionError(e, changedObjects); } ACTaskCategory newlyCreatedACTaskCategory = ACTaskCategory.process(acNewCategory, projectId, true); changedObjects.add(newlyCreatedACTaskCategory); acTaskCategoriesToAssign.add(newlyCreatedACTaskCategory); } protected static GHRepository findById(final long id) { checkArgument(id > 0); Optional<GHRepository> optionalGHRepository = Iterables .tryFind(MaidRoot.getInstance().getGhRepositoriesSet(), new Predicate<GHRepository>() { @Override public boolean apply(GHRepository ghRepository) { if (ghRepository == null) return false; return ghRepository.getId() == id; } }); return optionalGHRepository.isPresent() ? optionalGHRepository.get() : null; } @Override public String generateId() { if (getName() == null || getOwner() == null || getOwner().getLogin() == null) return null; return getOwner().getLogin() + "/" + getName(); } }