ProjectAres/PGM/src/main/java/tc/oc/pgm/map/MapModuleContext.java

134 lines
4.3 KiB
Java

package tc.oc.pgm.map;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Provider;
import com.google.common.collect.Range;
import com.google.common.hash.HashCode;
import org.jdom2.Document;
import tc.oc.api.docs.SemanticVersion;
import tc.oc.commons.core.util.Lazy;
import tc.oc.pgm.features.FeatureDefinitionContext;
import tc.oc.pgm.map.inject.MapScoped;
import tc.oc.pgm.module.ModuleContext;
import tc.oc.pgm.utils.XMLUtils;
import tc.oc.pgm.xml.InvalidXMLException;
import tc.oc.pgm.xml.Node;
import tc.oc.pgm.xml.UnsupportedMapProtocolException;
/**
* In addition to storing {@link MapModule}s, this class handles XML parsing from end to end.
*/
public class MapModuleContext extends ModuleContext<MapModule, MapScoped> {
private final Collection<Provider<MapRootParser>> parsers;
private final FeatureDefinitionContext featureDefinitionContext;
private final MapFilePreprocessor preprocessor;
private final Document xmlDocument;
private final SemanticVersion proto;
private final Path basePath;
private final Provider<MapDocument> apiDocumentProvider;
@Inject MapModuleContext(MapFolder mapFolder,
FeatureDefinitionContext featureDefinitionContext,
MapFilePreprocessor.Factory preprocessorFactory,
Provider<MapDocument> apiDocumentProvider,
Collection<Provider<MapRootParser>> parsers) throws InvalidXMLException {
this.featureDefinitionContext = featureDefinitionContext;
this.apiDocumentProvider = apiDocumentProvider;
this.parsers = parsers;
try {
this.basePath = mapFolder.getAbsolutePath().toRealPath();
} catch(IOException e) {
throw new InvalidXMLException("File system error while resolving map folder " + mapFolder);
}
final Path descriptionFile = mapFolder.getAbsoluteDescriptionFilePath();
if(!java.nio.file.Files.isRegularFile(descriptionFile)) {
throw new MapNotFoundException(descriptionFile);
}
this.preprocessor = preprocessorFactory.create(mapFolder.getSource());
this.xmlDocument = preprocessor.readRootDocument(descriptionFile);
// verify proto
final Node protoNode = Node.fromRequiredAttr(xmlDocument.getRootElement(), "proto");
this.proto = XMLUtils.parseSemanticVersion(protoNode);
if(proto.isNewerThan(ProtoVersions.CURRENT)) {
throw new UnsupportedMapProtocolException(protoNode, proto);
}
}
@Override
public void load() {
asCurrentScope(() -> {
// Create MapModules
super.load();
// Call MapParser#parse
parsers.forEach(
parser -> ignoringFailures(
parser.get()::parse
)
);
// Bail out early if there were any errors in the parse phase
if(hasErrors()) return;
// Resolve references and run validations
addErrors(featureDefinitionContext.postParse());
// Run module postParse methods
loadedModules().forEach(
module -> ignoringFailures(
() -> module.postParse(this, logger, xmlDocument)
)
);
});
}
public Document xmlDocument() {
return xmlDocument;
}
public MapDocument apiDocument() {
return asCurrentScope(apiDocumentProvider::get);
}
public Path getBasePath() {
return basePath;
}
public SemanticVersion getProto() {
return proto;
}
public FeatureDefinitionContext features() {
return this.featureDefinitionContext;
}
public Map<Path, HashCode> loadedFiles() {
return preprocessor.getIncludedFiles();
}
private final Lazy<Range<Integer>> playerLimits = Lazy.from(() -> {
int min = 0, max = 0;
for(MapModule module : loadedModules()) {
final Range<Integer> limits = module.getPlayerLimits();
min += limits.lowerEndpoint();
max += limits.upperEndpoint();
}
return Range.closed(min, max);
});
public Range<Integer> playerLimits() {
return playerLimits.get();
}
}