/*******************************************************************************
* Copyright (c) 2012-2015 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.project.server.type;
import org.eclipse.che.api.project.server.ProjectTypeConstraintException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.*;
import java.util.regex.Pattern;
/**
* @author gazarenkov
*/
@Singleton
public class ProjectTypeRegistry {
private static final Logger LOG = LoggerFactory.getLogger(ProjectTypeRegistry.class);
public static final ProjectType BASE_TYPE = new BaseProjectType();
public static final ChildToParentComparator CHILD_TO_PARENT_COMPARATOR = new ChildToParentComparator();
private final static Pattern NAME_PATTERN = Pattern.compile("[^a-zA-Z0-9-_.]");
private final Map<String, ProjectType> projectTypes = new HashMap<>();
private Set <String> allIds = new HashSet<>();
@Inject
public ProjectTypeRegistry(Set<ProjectType> projTypes) {
if(!projTypes.contains(BASE_TYPE)) {
allIds.add(BASE_TYPE.getId());
projectTypes.put(BASE_TYPE.getId(), BASE_TYPE);
}
for(ProjectType pt : projTypes) {
if(pt.getId() != null && !pt.getId().isEmpty()) {
allIds.add(pt.getId());
// add Base Type as a parent if not pointed
if (pt.getParents() != null && pt.getParents().isEmpty() && !pt.getId().equals(BASE_TYPE.getId())) {
LOG.debug("BASE added as parent of: " + pt.getId());
pt.getParents().add(BASE_TYPE);
}
}
}
for(ProjectType pt : projTypes) {
if(pt.getId() != null && !pt.getId().isEmpty()) {
try {
init(pt);
this.projectTypes.put(pt.getId(), pt);
} catch (ProjectTypeConstraintException e) {
LOG.error(e.getMessage());
}
}
}
}
public ProjectType getProjectType(String id) /*throws NotFoundException*/ {
ProjectType pt = projectTypes.get(id);
// TODO add this exception
// if(pt == null)
// throw new NotFoundException("Project Type "+id+" not found in the registry");
return pt;
}
public Collection <ProjectType> getProjectTypes() {
return projectTypes.values();
}
public List <ProjectType> getProjectTypes(Comparator<ProjectType> comparator) {
List<ProjectType> list = new ArrayList<>();
for(ProjectType pt : projectTypes.values()) {
list.add(pt);
}
Collections.sort(list, comparator);
return list;
}
// maybe for test only?
public void registerProjectType(ProjectType projectType) throws ProjectTypeConstraintException {
init(projectType);
this.projectTypes.put(projectType.getId(), projectType);
}
private void init(ProjectType pt) throws ProjectTypeConstraintException {
if(pt.getId() == null || pt.getId().isEmpty()) {
throw new ProjectTypeConstraintException("Could not register Project Type with null or empty ID: " + pt.getClass().getName());
}
// ID spelling (no spaces, only alphanumeric)
if(NAME_PATTERN.matcher(pt.getId()).find()) {
throw new ProjectTypeConstraintException("Could not register Project Type with invalid ID (only Alphanumeric, dash, point and underscore allowed): " +pt.getClass().getName()+ " ID: '"+pt.getId()+"'");
}
if(pt.getDisplayName() == null || pt.getDisplayName().isEmpty()) {
throw new ProjectTypeConstraintException("Could not register Project Type with null or empty display name: " +pt.getId());
}
for (Attribute attr : pt.getAttributes()) {
// ID spelling (no spaces, only alphanumeric)
if(NAME_PATTERN.matcher(attr.getName()).find()) {
throw new ProjectTypeConstraintException("Could not register Project Type with invalid attribute Name (only Alphanumeric, dash and underscore allowed): " +attr.getClass().getName()+ " ID: '"+attr.getId()+"'");
}
}
// look for parents
for(ProjectType parent : pt.getParents()) {
if(!allIds.contains(parent.getId())) {
throw new ProjectTypeConstraintException("Could not register Project Type: "+pt.getId()+" : Unregistered parent Type: "+parent.getId());
}
}
initAttributesRecursively(pt, pt);
// Builders and Runners
}
private void initAttributesRecursively(ProjectType myType, ProjectType type)
throws ProjectTypeConstraintException {
for(ProjectType supertype : type.getParents()) {
for(Attribute attr : supertype.getAttributes()) {
// check attribute names
for(Attribute attr2 : myType.getAttributes()) {
if(attr.getName().equals(attr2.getName()) && !attr.getProjectType().equals(attr2.getProjectType())) {
throw new ProjectTypeConstraintException("Attribute name conflict. Project type "+
myType.getId() + " could not be registered as attribute declaration "+ attr.getName()+
" is duplicated in its ancestor(s).");
}
}
((ProjectType)myType).addAttributeDefinition(attr);
}
//myType.addParent(supertype);
initAttributesRecursively(myType, supertype);
}
}
public static class ChildToParentComparator implements Comparator<ProjectType> {
@Override
public int compare(ProjectType o1, ProjectType o2) {
if(o1.isTypeOf(o2.getId())) {
return -1;
}
if(o2.isTypeOf(o1.getId())) {
return 1;
}
return 0;
}
}
}