/*******************************************************************************
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package hr.fer.zemris.vhdllab.service.impl;
import hr.fer.zemris.vhdllab.entity.File;
import hr.fer.zemris.vhdllab.entity.FileType;
import hr.fer.zemris.vhdllab.entity.PreferencesFile;
import hr.fer.zemris.vhdllab.entity.Project;
import hr.fer.zemris.vhdllab.service.Simulator;
import hr.fer.zemris.vhdllab.service.WorkspaceService;
import hr.fer.zemris.vhdllab.service.ci.CircuitInterface;
import hr.fer.zemris.vhdllab.service.hierarchy.Hierarchy;
import hr.fer.zemris.vhdllab.service.hierarchy.HierarchyNode;
import hr.fer.zemris.vhdllab.service.result.Result;
import hr.fer.zemris.vhdllab.service.util.SecurityUtils;
import hr.fer.zemris.vhdllab.service.workspace.FileReport;
import hr.fer.zemris.vhdllab.service.workspace.ProjectMetadata;
import hr.fer.zemris.vhdllab.service.workspace.Workspace;
import hr.fer.zemris.vhdllab.util.EntityUtils;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
public class WorkspaceServiceImpl extends ServiceSupport implements
WorkspaceService {
private static final Logger LOG = Logger.getLogger(WorkspaceServiceImpl.class);
@Autowired
private Simulator simulator;
@Override
public FileReport createFile(Integer projectId, String name, FileType type,
String data) {
File file = new File(name, type, data);
loadProject(projectId).addFile(file);
fileDao.persist(file);
return getReport(file, file.getProject());
}
@Override
public FileReport saveFile(Integer fileId, String data) {
File file = loadFile(fileId);
file.setData(data);
checkCircuitInterfaceName(file);
File saved = fileDao.merge(file);
return getReport(saved, saved.getProject());
}
@Override
public FileReport saveSimulation(Integer fileId, String name) {
Result result = simulator.simulate(fileId);
if (!StringUtils.isEmpty(result.getData())) {
File testbench = loadFile(fileId);
if (fileDao.findByName(testbench.getProject().getId(), name) == null) {
File simulation = new File(name, FileType.SIMULATION, result.getData());
testbench.getProject().addFile(simulation);
fileDao.persist(simulation);
return getReport(simulation, simulation.getProject());
}
}
return null;
}
private void checkCircuitInterfaceName(File file) {
if (FileType.SOURCE.equals(file.getType())) {
CircuitInterface ci = metadataExtractor
.extractCircuitInterface(file);
if (!ci.getName().equalsIgnoreCase(file.getName())) {
throw new IllegalStateException("Resource " + file.getName()
+ " must have only one entity with the same name.");
}
}
}
@Override
public FileReport deleteFile(Integer fileId) {
File file = loadFile(fileId);
// project reference will be removed during delete method
Project project = file.getProject();
fileDao.delete(file);
return getReport(file, project);
}
@Override
public File findByName(Integer projectId, String name) {
File file = findProjectOrPredefinedFile(projectId, name);
if (file == null) {
return null;
}
return new File(file, EntityUtils.lightweightClone(file.getProject()));
}
private FileReport getReport(File file, Project project) {
Hierarchy hierarchy = extractHierarchy(project.getId());
return new FileReport(file, hierarchy);
}
@Override
public Project persist(String name) {
Validate.notNull(name, "Project can't be null");
Project project = new Project(SecurityUtils.getUser(), name);
projectDao.persist(project);
return EntityUtils.lightweightClone(project);
}
@Override
public void deleteProject(Integer projectId) {
Project project = loadProject(projectId);
projectDao.delete(project);
}
@Override
public Hierarchy extractHierarchy(Integer projectId) {
Project project = loadProject(projectId);
Set<File> files = project.getFiles();
Map<File, HierarchyNode> resolvedNodes = new HashMap<File, HierarchyNode>(
files.size());
for (File file : files) {
if (resolvedNodes.containsKey(file)) {
continue;
}
HierarchyNode node = new HierarchyNode(file, null);
resolvedNodes.put(file, node);
resolveHierarchy(file, resolvedNodes);
}
Hierarchy hierarchy = new Hierarchy(project,
new HashSet<HierarchyNode>(resolvedNodes.values()));
return hierarchy;
}
/**
* Resolves a hierarchy tree for specified file. Resolved hierarchy trees
* gets added to <code>resolvedNodes</code> parameter.
*
* @param file
* a file for whom to resolve hierarchy tree
* @param resolvedNodes
* contains hierarchy tree for all resolved files
*/
private void resolveHierarchy(File file,
Map<File, HierarchyNode> resolvedNodes) {
/*
* This method resolves a hierarchy by recursively invoking itself.
*/
Set<String> dependencies;
try {
dependencies = metadataExtractor.extractDependencies(file);
} catch (Exception e) {
LOG.error("Error during extraction of hierarchy", e);
dependencies = Collections.emptySet();
}
for (String name : dependencies) {
File dep = findProjectOrPredefinedFile(file.getProject().getId(),
name);
if (dep == null) {
resolvedNodes.get(file).addMissingDependency(name);
continue;
}
HierarchyNode parent = resolvedNodes.get(file);
if (resolvedNodes.containsKey(dep)) {
HierarchyNode depNode = resolvedNodes.get(dep);
parent.addDependency(depNode);
} else {
HierarchyNode depNode = new HierarchyNode(dep, parent);
resolvedNodes.put(dep, depNode);
resolveHierarchy(dep, resolvedNodes);
}
}
}
@Override
public Workspace getWorkspace() {
String user = SecurityUtils.getUser();
List<Project> projects = projectDao.findByUser(user);
Map<Project, ProjectMetadata> projectMetadata = new LinkedHashMap<Project, ProjectMetadata>(
projects.size());
for (Project project : projects) {
// projectMetadata.put(project, null);
Hierarchy hierarchy = extractHierarchy(project.getId());
projectMetadata.put(project, new ProjectMetadata(hierarchy, project
.getFiles()));
}
// if (!projects.isEmpty()) {
// Project last = projects.get(projects.size() - 1);
// Hierarchy hierarchy = extractHierarchy(last.getId());
// projectMetadata.put(last, new ProjectMetadata(hierarchy, last
// .getFiles()));
// }
Set<File> predefinedFiles = predefinedFilesDao.getPredefinedFiles();
List<PreferencesFile> preferencesFiles = preferencesFileDao
.findByUser(user);
return new Workspace(projectMetadata, predefinedFiles, preferencesFiles);
}
}