/** * Copyright (c) 2001-2017 Mathew A. Nelson and Robocode contributors * 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://robocode.sourceforge.net/license/epl-v10.html */ package net.sf.robocode.repository; import net.sf.robocode.core.Container; import net.sf.robocode.io.FileUtil; import net.sf.robocode.io.Logger; import net.sf.robocode.io.URLJarCollector; import net.sf.robocode.repository.items.IRepositoryItem; import net.sf.robocode.repository.items.RobotItem; import net.sf.robocode.repository.items.TeamItem; import net.sf.robocode.repository.items.RepositoryItem; import net.sf.robocode.repository.packager.JarCreator; import net.sf.robocode.repository.root.IRepositoryRoot; import net.sf.robocode.repository.root.handlers.RootHandler; import net.sf.robocode.security.HiddenAccess; import net.sf.robocode.settings.ISettingsListener; import net.sf.robocode.settings.ISettingsManager; import net.sf.robocode.ui.IWindowManager; import robocode.control.RobotSpecification; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.StringTokenizer; /** * @author Pavel Savara (original) */ public class RepositoryManager implements IRepositoryManager { // NO_UCD (use default) private static final String DATABASE_FILENAME = "robot.database"; private final ISettingsManager properties; private Repository repository; public RepositoryManager(ISettingsManager properties) { // NO_UCD (unused code) this.properties = properties; properties.addPropertyListener(new SettingsListener()); } // ------------------------------------------ // interfaces // ------------------------------------------ @Override public File getRobotsDirectory() { return FileUtil.getRobotsDir(); } @Override public List<File> getDevelDirectories() { List<File> develDirectories = new ArrayList<File>(); for (String path : properties.getOptionsEnabledDevelopmentPaths()) { try { develDirectories.add(new File(path).getCanonicalFile()); } catch (IOException e) { Logger.logError(e); } } return develDirectories; } public void refresh(String friendlyUrl) { if (!updateItemRoot(friendlyUrl, true)) { refresh(true); } URLJarCollector.gc(); } public boolean refresh() { return refresh(false); } public boolean refresh(boolean force) { boolean refreshed = update(getRobotsDirectory(), getDevelDirectories(), force); if (refreshed) { setStatus("Saving robot database"); save(); } setStatus(""); URLJarCollector.gc(); return refreshed; } private boolean update(File robotsDir, Collection<File> devDirs, boolean force) { final int prev = repository.getItems().size(); RootHandler.openHandlers(); try { Map<String, IRepositoryRoot> newRoots = new HashMap<String, IRepositoryRoot>(); RootHandler.visitDirectories(robotsDir, false, newRoots, repository, force); for (File dir : devDirs) { RootHandler.visitDirectories(dir, true, newRoots, repository, force); } repository.setRoots(newRoots); } finally { RootHandler.closeHandlers(); } return prev != repository.getItems().size(); } private boolean updateItemRoot(String friendlyUrl, boolean force) { IRepositoryItem repositoryItem = repository.getItems().get(friendlyUrl); if (repositoryItem != null) { repositoryItem.getRoot().updateItem(repositoryItem, force); return true; } return false; } private void save() { FileOutputStream fos = null; try { fos = new FileOutputStream(new File(getRobotsDirectory(), DATABASE_FILENAME)); repository.save(fos); } catch (IOException e) { Logger.logError("Can't save robot database", e); } finally { FileUtil.cleanupStream(fos); } } private Repository load() { Repository repository = new Repository(); FileInputStream fis = null; try { File file = new File(getRobotsDirectory(), DATABASE_FILENAME); if (file.exists()) { fis = new FileInputStream(file); repository.load(fis); } } catch (IOException e) { Logger.logError("Can't load robot database", e); repository = null; } finally { FileUtil.cleanupStream(fis); } return repository; } public void reload(boolean rebuild) { // Bug fix [2867326] - Lockup on start if too many bots in robots dir (cont'd). URLJarCollector.enableGc(true); URLJarCollector.gc(); if (rebuild) { Logger.logMessage("Rebuilding robot database..."); repository = new Repository(); } else if (repository == null) { setStatus("Reading robot database"); repository = load(); if (repository == null) { setStatus("Building robot database"); repository = new Repository(); } } refresh(true); setStatus(""); } public RobotSpecification[] getSpecifications() { checkDbExists(); final Collection<IRobotSpecItem> list = getAllValidItems(); Collection<RobotSpecification> res = new ArrayList<RobotSpecification>(); for (IRobotSpecItem s : list) { res.add(s.createRobotSpecification()); } return res.toArray(new RobotSpecification[res.size()]); } /** * Expand teams, validate robots * @param selectedRobots, names of robots and teams, comma separated * @return robots in teams */ public RobotSpecification[] loadSelectedRobots(RobotSpecification[] selectedRobots) { checkDbExists(); Collection<RobotSpecification> battlingRobotsList = new ArrayList<RobotSpecification>(); int teamNum = 0; for (RobotSpecification spec: selectedRobots) { IRobotSpecItem item = (IRobotSpecItem) HiddenAccess.getFileSpecification(spec); if (item == null) { item = getRobot(spec.getNameAndVersion()); } loadItem(battlingRobotsList, spec, item, teamNum); teamNum++; } return battlingRobotsList.toArray(new RobotSpecification[battlingRobotsList.size()]); } /** * Expand teams, validate robots * @param selectedRobots, names of robots and teams, comma separated * @return robots in teams */ public RobotSpecification[] loadSelectedRobots(String selectedRobots) { checkDbExists(); Collection<RobotSpecification> battlingRobotsList = new ArrayList<RobotSpecification>(); final Collection<IRobotSpecItem> list = getValidItems(selectedRobots); int teamNum = 0; for (IRobotSpecItem item: list) { loadItem(battlingRobotsList, null, item, teamNum); teamNum++; } return battlingRobotsList.toArray(new RobotSpecification[battlingRobotsList.size()]); } private boolean loadItem(Collection<RobotSpecification> battlingRobotsList, RobotSpecification spec, IRobotSpecItem item, int teamNum) { String teamId = String.format("%4d", teamNum); if (item != null) { if (item.isTeam()) { teamId = item.getFullClassNameWithVersion() + "[" + teamId + "]"; final Collection<RobotItem> members = getRobotItems((TeamItem) item); for (IRobotSpecItem member : members) { final RobotItem robot = (RobotItem) member; boolean tested = false; for (RobotSpecification loaded : battlingRobotsList) { if (HiddenAccess.getFileSpecification(loaded).equals(robot)) { tested = true; break; } } if (tested || robot.validate()) { battlingRobotsList.add(robot.createRobotSpecification(null, teamId)); } } } else { final RobotItem robot = (RobotItem) item; if (robot.validate()) { battlingRobotsList.add(robot.createRobotSpecification(spec, null)); } else { Logger.logError("Could not load robot: " + robot.getFullClassName()); return false; } } return true; } return false; } public List<IRobotSpecItem> getSelectedSpecifications(String selectedRobots) { checkDbExists(); return getValidItems(selectedRobots); } private Collection<IRobotSpecItem> getAllValidItems() { final ArrayList<IRobotSpecItem> res = new ArrayList<IRobotSpecItem>(); for (IRepositoryItem repositoryItem : repository.getItems().values()) { final IRobotSpecItem spec = (IRobotSpecItem) repositoryItem; if (repositoryItem.isValid() && !res.contains(spec)) { res.add(spec); } } return res; } private List<IRobotSpecItem> getValidItems(String friendlyUrls) { List<IRobotSpecItem> result = new ArrayList<IRobotSpecItem>(); StringTokenizer tokenizer = new StringTokenizer(friendlyUrls, ","); while (tokenizer.hasMoreTokens()) { String friendlyUrl = tokenizer.nextToken().trim(); IRepositoryItem repositoryItem = repository.getItem(friendlyUrl); if (repositoryItem != null) { if (repositoryItem.isValid()) { result.add((IRobotSpecItem) repositoryItem); } else { Logger.logError("Can't load '" + friendlyUrl + "' because it is an invalid robot or team."); } } else { Logger.logError("Can't find '" + friendlyUrl + '\''); } } return result; } public List<IRobotSpecItem> getRepositoryItems(boolean onlyWithSource, boolean onlyWithPackage, boolean onlyRobots, boolean onlyDevelopment, boolean onlyNotDevelopment, boolean ignoreTeamRobots, boolean onlyInJar) { checkDbExists(); final List<IRobotSpecItem> res = new ArrayList<IRobotSpecItem>(); for (IRepositoryItem repositoryItem : repository.getItems().values()) { final IRobotSpecItem spec = (IRobotSpecItem) repositoryItem; if (!repositoryItem.isValid()) { continue; } if (onlyWithSource && !spec.isSourceIncluded()) { continue; } if (onlyWithPackage && spec.getFullPackage() == null) { continue; } if (onlyInJar && !spec.isInJAR()) { continue; } if (onlyRobots && !(repositoryItem instanceof RobotItem)) { continue; } if (onlyDevelopment && !spec.isDevelopmentVersion()) { continue; } if (onlyNotDevelopment && spec.isDevelopmentVersion()) { continue; } if (res.contains(spec)) { continue; } res.add(spec); } Collections.sort(res); return res; } public boolean verifyRobotName(String robotName, String shortClassName) { return RobotItem.verifyRobotName(robotName, shortClassName, true); } public int extractJar(IRobotSpecItem item) { if (!item.isInJAR()) { return -2; } ((RepositoryItem) item).getRoot().extractJAR(); return 0; } public void createTeam(File target, TeamProperties teamProps) throws IOException { checkDbExists(); TeamItem.createOrUpdateTeam(target, teamProps); refresh(target.toURI().toString()); } public String createPackage(File jarFile, List<IRobotSpecItem> selectedRobots, RobotProperties robotProps) { checkDbExists(); List<RobotItem> robotItems = getAllRobotItems(selectedRobots); TeamItem teamItem = getTeamItem(selectedRobots); String res = JarCreator.createPackage(jarFile, robotItems, teamItem, robotProps); refresh(jarFile.toURI().toString()); return res; } private Collection<RobotItem> getRobotItems(TeamItem team) { Collection<RobotItem> result = new ArrayList<RobotItem>(); StringTokenizer teamTokenizer = new StringTokenizer(team.getMembers(), ","); while (teamTokenizer.hasMoreTokens()) { String botNameAndVersion = teamTokenizer.nextToken(); int versionIndex = botNameAndVersion.indexOf(' '); String botPath = versionIndex < 0 ? botNameAndVersion : botNameAndVersion.substring(0, versionIndex); botPath = botPath.replace('.', '/').replaceAll("\\*", ""); // first load from same classPath String teamBot = team.getRoot().getURL() + botPath; IRepositoryItem res = repository.getItem(teamBot); if (res != null && res instanceof RobotItem) { result.add((RobotItem) res); continue; } // try general search res = repository.getItem(botNameAndVersion); if (res != null && res instanceof RobotItem) { result.add((RobotItem) res); continue; } // not found Logger.logError("Can't find robot: " + botNameAndVersion); } return result; } private List<RobotItem> getAllRobotItems(Collection<IRobotSpecItem> items) { List<RobotItem> result = new ArrayList<RobotItem>(); for (IRobotSpecItem item : items) { if (item.isTeam()) { result.addAll(getRobotItems((TeamItem) item)); } else { result.add((RobotItem) item); } } return result; } private static TeamItem getTeamItem(Collection<IRobotSpecItem> items) { for (IRobotSpecItem item : items) { if (item.isTeam()) { return (TeamItem) item; } } return null; } private IRobotSpecItem getRobot(String fullClassNameWithVersion) { final IRepositoryItem repositoryItem = repository.getItem(fullClassNameWithVersion); if (repositoryItem == null || !repositoryItem.isValid()) { return null; } return (IRobotSpecItem) repositoryItem; } private void setStatus(String message) { IWindowManager windowManager = Container.getComponent(IWindowManager.class); if (windowManager != null) { windowManager.setStatus(message); } if (message.length() > 0) { Logger.logMessage(message); } } private void checkDbExists() { if (repository == null) { reload(false); } } // ------------------------------------------ // settings listener // ------------------------------------------ private class SettingsListener implements ISettingsListener { private Collection<String> lastEnabledDevelPaths; public void settingChanged(String property) { if (property.equals(ISettingsManager.OPTIONS_DEVELOPMENT_PATH) || property.equals(ISettingsManager.OPTIONS_DEVELOPMENT_PATH_EXCLUDED)) { Collection<String> enabledDevelPaths = properties.getOptionsEnabledDevelopmentPaths(); if (lastEnabledDevelPaths == null || !enabledDevelPaths.equals(lastEnabledDevelPaths)) { lastEnabledDevelPaths = enabledDevelPaths; reload(true); } } } } }