/*******************************************************************************
* PSHDL is a library and (trans-)compiler for PSHDL input. It generates
* output suitable for implementation or simulation of it.
*
* Copyright (C) 2013 Karsten Becker (feedback (at) pshdl (dot) org)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* This License does not grant permission to use the trade names, trademarks,
* service marks, or product names of the Licensor, except as required for
* reasonable and customary use in describing the origin of the Work.
*
* Contributors:
* Karsten Becker - initial API and implementation
******************************************************************************/
package org.pshdl.model.utils;
import static org.pshdl.model.extensions.FullNameExtension.fullNameOf;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import org.pshdl.model.HDLDeclaration;
import org.pshdl.model.HDLEnum;
import org.pshdl.model.HDLEnumDeclaration;
import org.pshdl.model.HDLFunction;
import org.pshdl.model.HDLInterface;
import org.pshdl.model.HDLInterfaceDeclaration;
import org.pshdl.model.HDLObject;
import org.pshdl.model.HDLObject.GenericMeta;
import org.pshdl.model.HDLPackage;
import org.pshdl.model.HDLType;
import org.pshdl.model.HDLUnit;
import org.pshdl.model.HDLVariable;
import org.pshdl.model.HDLVariableDeclaration;
import org.pshdl.model.IHDLObject;
import org.pshdl.model.types.builtIn.HDLFunctions;
import org.pshdl.model.types.builtIn.PSHDLLib;
import org.pshdl.model.utils.services.AuxiliaryContent;
import com.google.common.base.Optional;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
public class HDLLibrary {
private static class Record {
public static enum RecordType {
function, type, unit, variable
};
public final HDLQualifiedName ref;
public final String src;
public final RecordType type;
public final HDLFunction function;
public Record(String src, RecordType type, HDLQualifiedName ref, HDLFunction function) {
super();
this.src = src;
this.type = type;
this.ref = ref;
this.function = function;
}
}
private static Map<String, HDLLibrary> libs = Maps.newLinkedHashMap();
public static HDLLibrary getLibrary(String libURI) {
if (libURI == null)
throw new IllegalArgumentException("Library uri can not be null");
final HDLLibrary hdlLibrary = libs.get(libURI);
return hdlLibrary;
}
public static void registerLibrary(String libURI, HDLLibrary library) {
if (libURI == null)
throw new IllegalArgumentException("Library uri can not be null");
final HDLLibrary put = libs.put(libURI, library);
if ((put != null) && (put != library))
throw new IllegalArgumentException("A library with this id already exists!");
}
public static void unregister(String libURI) {
if (libURI == null)
throw new IllegalArgumentException("Library uri can not be null");
libs.remove(libURI);
}
public final Multimap<HDLQualifiedName, HDLFunction> functions = Multimaps.synchronizedSetMultimap(LinkedHashMultimap.<HDLQualifiedName, HDLFunction> create());
public final Multimap<String, Record> objects = LinkedListMultimap.create();
public final Multimap<String, AuxiliaryContent> sideFiles = LinkedListMultimap.create();
public final Map<HDLQualifiedName, HDLType> types = new ConcurrentHashMap<HDLQualifiedName, HDLType>();
public final Map<HDLQualifiedName, HDLUnit> units = new ConcurrentHashMap<HDLQualifiedName, HDLUnit>();
public final Map<HDLQualifiedName, HDLVariable> variables = new ConcurrentHashMap<HDLQualifiedName, HDLVariable>();
public HDLLibrary() {
addPkg(PSHDLLib.getLib(), "#PSHDLLib");
HDLFunctions.initLibrary(this);
}
/**
* Adds the given enum to the library so that it can be resolved by
* {@link #resolve(Iterable, HDLQualifiedName)}
*
* @param hEnum
*/
public void addEnum(HDLEnum hEnum, String src) {
checkFrozen(hEnum);
final HDLQualifiedName fqn = fullNameOf(hEnum);
types.put(fqn, hEnum);
addRecord(src, new Record(src, Record.RecordType.type, fqn, null));
}
private void addRecord(String src, Record record) {
final Collection<Record> collection = objects.get(src);
for (final Iterator<Record> iter = collection.iterator(); iter.hasNext();) {
final Record rec = iter.next();
if (rec.type == record.type) {
iter.remove();
}
}
objects.put(src, record);
}
/**
* Adds the given function to the library so that it can be resolved by
* {@link #resolveFunction(Iterable, HDLQualifiedName)}
*
* @param func
*/
public void addFunction(HDLFunction func, String src) {
checkFrozen(func);
final HDLQualifiedName fqn = fullNameOf(func);
functions.put(fqn, Insulin.resolveFragments(func));
addRecord(src, new Record(src, Record.RecordType.function, fqn, func));
}
/**
* Adds the given interface to the library so that it can be resolved by
* {@link #resolve(Iterable, HDLQualifiedName)}
*
* @param hIf
*/
public void addInterface(HDLInterface hIf, String src) {
checkFrozen(hIf);
final HDLQualifiedName fqn = fullNameOf(hIf);
types.put(fqn, hIf);
addRecord(src, new Record(src, Record.RecordType.type, fqn, null));
}
/**
* Adds a package to this library. This includes all units and declarations
*
* @param pkg
* the package to be added
* @param src
* the source from which this package was derived
*/
public void addPkg(HDLPackage pkg, String src) {
checkFrozen(pkg);
for (final HDLUnit unit : pkg.getUnits()) {
final HDLQualifiedName uq = fullNameOf(unit);
addRecord(src, new Record(src, Record.RecordType.unit, uq, null));
units.put(uq, unit);
addInterface(unit.asInterface(), uq.toString());
final HDLInterface[] list = unit.getAllObjectsOf(HDLInterface.class, true);
for (final HDLInterface hdlInterface : list) {
addInterface(hdlInterface, src);
}
final HDLEnum[] elist = unit.getAllObjectsOf(HDLEnum.class, true);
for (final HDLEnum hdlEnum : elist) {
addEnum(hdlEnum, src);
}
final HDLFunction[] functions = unit.getAllObjectsOf(HDLFunction.class, true);
for (final HDLFunction hdlFunction : functions) {
addFunction(hdlFunction, src);
}
}
for (final HDLDeclaration decl : pkg.getDeclarations()) {
switch (decl.getClassType()) {
case HDLEnumDeclaration:
final HDLEnumDeclaration ed = (HDLEnumDeclaration) decl;
addEnum(ed.getHEnum(), src);
break;
case HDLInterfaceDeclaration:
final HDLInterfaceDeclaration hid = (HDLInterfaceDeclaration) decl;
addInterface(hid.getHIf(), src);
break;
case HDLVariableDeclaration:
final HDLVariableDeclaration hvd = (HDLVariableDeclaration) decl;
for (final HDLVariable var : hvd.getVariables()) {
addVariable(var, src);
}
break;
default:
if (decl instanceof HDLFunction) {
final HDLFunction func = (HDLFunction) decl;
addFunction(func, src);
} else
throw new IllegalArgumentException("Did not handle:" + decl);
}
}
}
/**
* Add generated files to this library so that it can be retrieved later
*
* @param files
* @param src
*/
public void addSideFiles(List<AuxiliaryContent> files, String src) {
sideFiles.putAll(src, files);
}
/**
* Adds the given variable to the library so that it can be resolved by
* {@link #resolveVariable(Iterable, HDLQualifiedName)}
*
* @param var
*/
public void addVariable(HDLVariable var, String src) {
checkFrozen(var);
final HDLQualifiedName fqn = fullNameOf(var);
variables.put(fqn, var);
addRecord(src, new Record(src, Record.RecordType.variable, fqn, null));
}
private void checkFrozen(IHDLObject hObject) {
if (!hObject.isFrozen())
throw new IllegalArgumentException("Objects need to be frozen to be added");
}
private <T extends IHDLObject> Optional<Iterable<T>> checkGenericImport(HDLQualifiedName type, String string, Multimap<HDLQualifiedName, T> map) {
final HDLQualifiedName newTypeName = new HDLQualifiedName(string).skipLast(1).append(type);
final Iterable<T> newType = map.get(newTypeName);
if (newType.iterator().hasNext())
return Optional.of(newType);
return Optional.absent();
}
private <T extends IHDLObject> Optional<T> checkGenericImport(HDLQualifiedName type, String string, Map<HDLQualifiedName, T> map) {
final HDLQualifiedName newTypeName = new HDLQualifiedName(string).skipLast(1).append(type);
final T newType = map.get(newTypeName);
if (newType != null)
return Optional.of(Insulin.resolveFragments(newType));
return Optional.absent();
}
/**
* Removes all types, variables, functions etc. which were derived from the
* given src. This is useful for incremental compilation.
*
* @param src
*/
public void removeAllSrc(String src) {
final Collection<Record> collection = objects.get(src);
for (final Record record : collection) {
switch (record.type) {
case function:
functions.remove(record.ref, record.function);
break;
case type:
types.remove(record.ref);
break;
case unit:
units.remove(record.ref);
types.remove(record.ref);
break;
case variable:
variables.remove(record.ref);
break;
}
}
objects.removeAll(src);
}
public static ThreadLocal<Boolean> resolveFragments = new ThreadLocal<>();
/**
* Resolves a type by firstly checking if it already exists given the
* qualified name. If not the specific imports are tried first, then the
* wild card ones in order of declaration.
*
* @param imports
* a list of specific and wild card imports
* @param type
* the fqn or local name of the type to look for
* @return the type if found
*/
public Optional<? extends HDLType> resolve(Iterable<String> imports, HDLQualifiedName type) {
HDLType hdlType = types.get(type);
if (hdlType == null) {
for (final String string : imports)
if (string.endsWith(type.toString())) {
hdlType = types.get(new HDLQualifiedName(string));
break;
}
}
if (hdlType == null) {
final Optional<HDLType> genericImport = checkGenericImport(type, "pshdl.*", types);
if (genericImport.isPresent()) {
hdlType = genericImport.get();
}
}
if (hdlType == null) {
for (final String string : imports)
if (string.endsWith(".*")) {
final Optional<HDLType> genericImport = checkGenericImport(type, string, types);
if (genericImport.isPresent()) {
hdlType = genericImport.get();
break;
}
}
}
if (hdlType != null) {
if ((resolveFragments.get() == null) || resolveFragments.get())
return Optional.fromNullable(Insulin.resolveFragments(hdlType));
return Optional.of(hdlType);
}
return Optional.absent();
}
/**
* Resolves a type by firstly checking if it already exists given the
* qualified name. If not the specific imports are tried first, then the
* wild card ones in order of declaration.
*
* @param imports
* a list of specific and wild card imports
* @param type
* the fqn or local name of the type to look for
* @return the type if found
*/
public Optional<Iterable<HDLFunction>> resolveFunction(Iterable<String> imports, HDLQualifiedName type) {
Iterable<HDLFunction> hdlFunction = functions.get(type);
if (hdlFunction.iterator().hasNext())
return Optional.of(hdlFunction);
for (final String string : imports) {
if (string.endsWith(type.toString())) {
hdlFunction = functions.get(new HDLQualifiedName(string));
if (hdlFunction.iterator().hasNext())
return Optional.of(hdlFunction);
}
}
Optional<Iterable<HDLFunction>> genericImport = checkGenericImport(type, "pshdl.*", functions);
if (genericImport.isPresent())
return genericImport;
for (final String string : imports) {
if (string.endsWith(".*")) {
genericImport = checkGenericImport(type, string, functions);
if (genericImport.isPresent())
return genericImport;
}
}
return Optional.absent();
}
/**
* Resolves a type by firstly checking if it already exists given the
* qualified name. If not the specific imports are tried first, then the
* wild card ones in order of declaration.
*
* @param imports
* a list of specific and wild card imports
* @param type
* the fqn or local name of the type to look for
* @return the type if found
*/
public Optional<HDLVariable> resolveVariable(Iterable<String> imports, HDLQualifiedName type) {
HDLVariable hdlVariable = variables.get(type);
if (hdlVariable == null) {
for (final String string : imports)
if (string.endsWith(type.toString())) {
hdlVariable = variables.get(new HDLQualifiedName(string));
if (hdlVariable != null)
return Optional.fromNullable(Insulin.resolveFragments(hdlVariable));
}
Optional<HDLVariable> genericImport = checkGenericImport(type, "pshdl.*", variables);
if (genericImport.isPresent())
return genericImport;
for (final String string : imports)
if (string.endsWith(".*")) {
genericImport = checkGenericImport(type, string, variables);
if (genericImport.isPresent())
return genericImport;
}
}
if (hdlVariable != null)
return Optional.fromNullable(Insulin.resolveFragments(hdlVariable));
return Optional.absent();
}
public void unregister() {
final Iterator<Entry<String, HDLLibrary>> iter = libs.entrySet().iterator();
while (iter.hasNext()) {
final Entry<String, HDLLibrary> e = iter.next();
if (e.getValue() == this) {
iter.remove();
}
}
}
public Map<MetaAccess<?>, Object> metaData = Maps.newLinkedHashMap();
public void addMeta(String key, Object value) {
metaData.put(new GenericMeta<Object>(key, true), value);
}
public Object getMeta(String key) {
return metaData.get(new GenericMeta<Object>(key, true));
}
public <K> void addMeta(MetaAccess<K> key, K value) {
metaData.put(key, value);
}
public void setMeta(MetaAccess<Boolean> meta) {
addMeta(meta, true);
}
public boolean hasMeta(MetaAccess<?> key) {
return getMeta(key) != null;
}
@SuppressWarnings("unchecked")
public <K> K getMeta(MetaAccess<K> key) {
return (K) metaData.get(key);
}
public HDLUnit getUnit(HDLQualifiedName asRef) {
return units.get(asRef);
}
public String getSrc(HDLQualifiedName asRef) {
for (final Record r : objects.values()) {
if (r.ref.equals(asRef))
return r.src;
}
return null;
}
public HDLPackage getAsPackage(String libURI) {
HDLPackage pkg = new HDLPackage().setLibURI(libURI);
for (final Entry<HDLQualifiedName, HDLVariable> e : variables.entrySet()) {
if (e.getKey().getSegment(0).equals("pshdl")) {
continue;
}
final HDLVariable vars = e.getValue();
final HDLVariableDeclaration hvd = vars.getContainer(HDLVariableDeclaration.class);
if (hvd == null)
throw new IllegalArgumentException("The variable: " + vars.getName() + " does not have a declaration");
pkg = pkg.addDeclarations(hvd.setVariables(HDLObject.asList(vars.setName(e.getKey().toString()))));
}
for (final Entry<HDLQualifiedName, HDLFunction> e : functions.entries()) {
if (e.getKey().getSegment(0).equals("pshdl")) {
continue;
}
final HDLFunction function = e.getValue();
pkg = pkg.addDeclarations(function);
}
for (final Entry<HDLQualifiedName, HDLType> e : types.entrySet()) {
if (e.getKey().getSegment(0).equals("pshdl")) {
continue;
}
final HDLType type = e.getValue();
if (type.getContainer(HDLUnit.class) != null) {
continue;
}
switch (type.getClassType()) {
case HDLInterface:
if (!units.containsKey(e.getKey())) {
pkg = pkg.addDeclarations(new HDLInterfaceDeclaration().setHIf((HDLInterface) type.setName(e.getKey().toString())));
}
break;
case HDLEnum:
pkg = pkg.addDeclarations(new HDLEnumDeclaration().setHEnum((HDLEnum) type.setName(e.getKey().toString())));
break;
default:
throw new IllegalArgumentException("Did not expect type:" + type);
}
}
for (final Entry<HDLQualifiedName, HDLUnit> e : units.entrySet()) {
if (e.getKey().getSegment(0).equals("pshdl")) {
continue;
}
final HDLUnit unit = e.getValue();
pkg = pkg.addUnits(unit.setName(e.getKey().toString()));
}
return pkg.copyDeepFrozen(null);
}
}