/*
* mesopotamia @mesopotamia.version@
* Multilingual parser and repository.
* Copyright (C) 2005 Hammurapi Group
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* URL: http://http://www.hammurapi.biz
* e-Mail: support@hammurapi.biz
*/
package org.mesopotamia;
import gnu.trove.TIntHashSet;
import java.awt.event.WindowEvent;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.swing.WindowConstants;
import org.mesopotamia.sql.MesopotamiaEngine;
import org.mesopotamia.util.MesopotamiaVisualizer;
import biz.hammurapi.cache.Cache;
import biz.hammurapi.cache.Entry;
import biz.hammurapi.cache.MemoryCache;
import biz.hammurapi.cache.Producer;
import biz.hammurapi.convert.Converter;
import biz.hammurapi.metrics.MeasurementCategory;
import biz.hammurapi.metrics.MeasurementCategoryFactory;
import biz.hammurapi.sql.IdentityGenerator;
import biz.hammurapi.sql.IdentityManager;
import biz.hammurapi.sql.IdentityRetriever;
import biz.hammurapi.sql.SQLProcessor;
import biz.hammurapi.sql.Transaction;
import biz.hammurapi.swing.Browser;
import biz.hammurapi.swing.CompositeVisualizer;
import biz.hammurapi.swing.Visualizable;
import biz.hammurapi.util.CollectionVisitable;
import biz.hammurapi.util.ConvertingCollection;
import biz.hammurapi.util.VisitableBase;
import biz.hammurapi.util.Visitor;
public class Scan extends VisitableBase {
private static final MeasurementCategory mc=MeasurementCategoryFactory.getCategory(Repository.class);
private Repository repository;
private org.mesopotamia.sql.Scan dbData;
Object environment;
private class SourceUnitProducer implements Producer {
public Entry get(Object key) {
Integer sid=(Integer) key;
MesopotamiaEngine engine=repository.getFactory().getEngine();
try {
org.mesopotamia.sql.SourceUnit dbData=engine.getSourceUnit(sid.intValue());
if (dbData==null) {
throw new MesopotamiaRuntimeException("Invalid source unit ID: "+sid);
}
RepositoryLanguage repoLanguage=repository.getFactory().getRepositoryLanguage(new Language(dbData.getLanguage(), dbData.getLanguageVersion(), null));
if (repoLanguage==null) {
throw new MesopotamiaRuntimeException("Language not found: "+dbData.getLanguage()+" "+dbData.getLanguageVersion());
}
final SourceUnit su = repoLanguage.instantiateSourceUnit(dbData, Scan.this);
return new Entry() {
public long getExpirationTime() {
return 0;
}
public long getTime() {
return 0;
}
public Object get() {
return su;
}
};
} catch (SQLException e) {
throw new MesopotamiaRuntimeException("Cannot instantiate source unit", e);
}
}
public void addCache(Cache cache) {
// Empty method
}
public Set keySet() {
// Empty method
return null;
}
}
private class LanguageElementProducer implements Producer {
public Entry get(Object key) {
LanguageElementHandle lek=(LanguageElementHandle) key;
SourceUnit su = getSourceUnit(lek.getSourceUnitId());
if (su==null) {
throw new MesopotamiaRuntimeException("Invalid source unit ID: "+lek.getSourceUnitId());
}
SyntaxTree st = (SyntaxTree) su.getLevelData("ast");
if (st==null) {
throw new MesopotamiaRuntimeException("Source unit "+lek.getSourceUnitId()+" is not loaded at level ast");
}
org.mesopotamia.MesopotamiaNode xData=st.findNode(lek.getId());
if (xData==null) {
throw new MesopotamiaRuntimeException("Invalid language element ID: "+lek.getId());
}
RepositoryLanguage repoLanguage=repository.getFactory().getRepositoryLanguageByTokenTypeId(xData.getType());
if (repoLanguage==null) {
throw new MesopotamiaRuntimeException("Language not found for token type id: "+xData.getType());
}
final LanguageElement le = repoLanguage.instantiateLanguageElement(xData, lek.getContextClass(), lek.getTargetClass(), Scan.this);
return new Entry() {
public long getExpirationTime() {
return 0;
}
public long getTime() {
return 0;
}
public Object get() {
return le;
}
};
}
public void addCache(Cache cache) {
// Empty method
}
public Set keySet() {
// Empty method
return null;
}
}
/**
* Repository is instantiated only by RepositoryFactory
* @param environment
* @param checkSumName
* @param selector
* @param sourceIterator
* @param listener
* @param factory
* @param language
* @param languageVersion
* @param name
*/
Scan(
Repository repository,
org.mesopotamia.sql.Scan dbData,
SourceIterator sourceIterator,
LanguageSelector selector,
String checkSumAlgorithm,
final Object environment,
final LoadListener listener)
throws MesopotamiaException {
this(repository, dbData);
this.environment=environment;
if (listener!=null) {
listener.onScanStarted(dbData.getId());
}
final RepositoryFactory factory = repository.getFactory();
int sourceCounter=0;
Source s;
while ((s=sourceIterator.nextSource())!=null) {
Language language = selector.select(s);
if (language==null) {
continue; // No language associated with extension
}
final RepositoryLanguage repoLanguage = factory.getRepositoryLanguage(language);
if (repoLanguage==null) {
continue; // Given language is not defined in the repository
}
++sourceCounter;
final SourceLink sl=linkSource(repoLanguage, s, checkSumAlgorithm);
final int scanId = Scan.this.dbData.getId();
Iterator lit=repoLanguage.getLoaders().iterator();
while (lit.hasNext()) {
final LoaderEntry le = (LoaderEntry) lit.next();
final String levelName = le.getLevel();
if (sl.loadLevels.contains(le.getId())) {
listener.onLink(scanId, sl.sourceId, levelName);
}
}
// TODO - in the future analyze which levels are not yet loaded
// and invoke load only if there are unloaded levels.
// it is done in load() anyway
repoLanguage.load(scanId, sl.sourceId, s, environment, listener);
}
if (listener!=null) {
listener.onScanFinished(dbData.getId(), sourceCounter);
}
}
/**
* Repository is instantiated only by RepositoryFactory
* @param environment
* @param checkSumName
* @param selector
* @param sourceIterator
* @param listener
* @param factory
* @param language
* @param languageVersion
* @param name
*/
Scan(final Repository repository, org.mesopotamia.sql.Scan dbData) {
this.repository=repository;
this.dbData=dbData;
languageElementsCache=new MemoryCache(
new LanguageElementProducer(),
null,
mc,
repository.getFactory().getTimer(),
MemoryCache.CLEANUP_INTERVAL);
sourceUnitCache=new MemoryCache(
new SourceUnitProducer(),
null,
mc,
repository.getFactory().getTimer(),
MemoryCache.CLEANUP_INTERVAL);
sourceUnitLoadLevelsCache=new MemoryCache(
new Producer() {
public Entry get(Object key) {
try {
final Collection value = repository.getFactory().getEngine().getSourceUnitSuccessfulLoadLevels(
((Number) key).intValue(),
getId(),
new HashSet(),
repository.getFactory().levelIdToIntegerConverter);
return new Entry() {
public long getExpirationTime() {
return 0;
}
public long getTime() {
return 0;
}
public Object get() {
return value;
}
};
} catch (SQLException e) {
throw new MesopotamiaRuntimeException("Cannol retrieve source unit load levels from the database", e);
}
}
public void addCache(Cache cache) {
// Empty method
}
public Set keySet() {
// Empty method
return null;
}
},
null,
mc,
repository.getFactory().getTimer(),
MemoryCache.CLEANUP_INTERVAL);
}
private class SourceLink {
Source source;
int sourceId;
public TIntHashSet loadLevels;
}
/**
* @param sources
* @param digestAlgorithm
* @param factory
* @throws MesopotamiaException
*/
private SourceLink linkSource(
RepositoryLanguage repoLanguage,
Source source,
String digestAlgorithm) throws MesopotamiaException {
try {
final RepositoryFactory factory = repoLanguage.getFactory();
String digest = source.getDigest(factory.getMessageDigest(digestAlgorithm));
final org.mesopotamia.sql.SourceUnit[] su = {factory.getEngine().getExistingSourceUnit(
repository.getId(),
source.getSize(),
digest,
source.getPath())};
TIntHashSet loadLevels=new TIntHashSet();
if (su[0]==null) {
// TODO - For changed source set reference to previous revision
su[0]=new org.mesopotamia.sql.SourceUnitImpl(true);
su[0].setDigestAlgorithm(digestAlgorithm);
su[0].setName(source.getName());
su[0].setPath(source.getPath());
su[0].setUnitDigest(digest);
su[0].setUnitSize(new Long(source.getSize()));
su[0].setLastModified(new Timestamp(source.getLastModified()));
su[0].setLanguage(repoLanguage.getName());
su[0].setLanguageVersion(repoLanguage.getVersion());
su[0].setRepositoryId(repository.getId());
factory.getProcessor().executeTransaction(new Transaction() {
public boolean execute(SQLProcessor processor) throws SQLException {
IdentityManager identityManager = factory.getIdentityManager();
if (identityManager instanceof IdentityGenerator) {
su[0].setId(((IdentityGenerator) identityManager).generate(processor.getConnection(), "REPOSITORY"));
}
new MesopotamiaEngine(processor).insertSourceUnit(su[0]);
if (identityManager instanceof IdentityRetriever) {
su[0].setId(((IdentityRetriever) identityManager).retrieve(processor.getConnection()));
}
return true;
}
});
}
factory.getEngine().insertSourceUnitScan(getId(), su[0].getId());
SourceLink sl=new SourceLink();
sl.source=source;
sl.sourceId=su[0].getId();
sl.loadLevels=loadLevels;
return sl;
} catch (SQLException e) {
throw new MesopotamiaException(e);
}
}
// Should be instantiated by repository
// dbData - Scan, expose its accessors through
// delegation
protected void acceptChildren(Visitor visitor) {
try {
new CollectionVisitable(getSourceUnits(), false).accept(visitor);
} catch (MesopotamiaException e) {
throw new MesopotamiaRuntimeException(e);
}
// For languages which do not support namespaces
// or where one source unit can contain multiple
// namespaces (C++)
// iterate through source units
// otherwise iterate through namespaces
// which will iterate through source units
// TODO Auto-generated method stub
}
public String getDescription() {
return dbData.getDescription();
}
public int getId() {
return dbData.getId();
}
public Repository getRepository() {
return repository;
}
public Timestamp getScanDate() {
return dbData.getScanDate();
}
public void setDescription(String description) {
dbData.setDescription(description);
}
public void delete() throws MesopotamiaException {
MesopotamiaEngine engine = repository.getFactory().getEngine();
try {
engine.deleteSourceUnitScanByScan(getId());
engine.deleteScan(getId());
engine.deleteOrphanSourceUnits(repository.getId());
} catch (SQLException e) {
throw new MesopotamiaException("Scan detetion failed: "+e, e);
}
}
/**
* Memory cache is a front-end for language elment producer.
* This approach guarantees that at any point of time there is only one
* instance of a particular language element in memory
*/
private MemoryCache languageElementsCache;
/**
* Memory cache is a front-end for source unit producer.
* This approach guarantees that at any point of time there is only one
* instance of a particular source unit in memory
*/
private MemoryCache sourceUnitCache;
public LanguageElement getLanguageElement(LanguageElementHandle handle) {
if (handle==null) {
return null;
}
Entry e=languageElementsCache.get(handle);
LanguageElement ret = (LanguageElement) (e==null ? null : e.get());
// if (ret!=null && ret.getSourceUnit().getId()!=handle.getSourceUnitId()) {
// throw new IllegalArgumentException("Handle and return are from different source units, "+handle+", "+ret);
// }
return ret;
}
public SourceUnit getSourceUnit(int sourceUnitId) {
Entry e=sourceUnitCache.get(new Integer(sourceUnitId));
return (SourceUnit) (e==null ? null : e.get());
}
private Collection sourceUnits;
public Collection getSourceUnits() throws MesopotamiaException {
if (sourceUnits==null) {
try {
Collection suIds = repository.getFactory().getEngine().getSourceUnitsInScan(
dbData.getId(),
new ArrayList(),
repository.getFactory().toIntegerConverter);
sourceUnits=Collections.unmodifiableCollection(
new ConvertingCollection(
suIds,
new Converter() {
public Object convert(Object source) {
return getSourceUnit(((Number) source).intValue());
}
},
new Converter() {
public Object convert(Object source) {
return new Integer(((SourceUnit) source).getId());
}
}));
} catch (SQLException e) {
throw new MesopotamiaException("Cannot load source unit id's", e);
}
}
return sourceUnits;
}
/**
* Shuts down language element and source unit caches
*/
public void stop() {
languageElementsCache.stop();
sourceUnitCache.stop();
sourceUnitLoadLevelsCache.stop();
}
public Namespace getNamespace(Integer namespaceId) {
// TODO Auto-generated method stub
return null;
}
private MemoryCache sourceUnitLoadLevelsCache;
public Collection getSourceUnitLoadLevels(int id) {
return (Collection) sourceUnitLoadLevelsCache.get(new Integer(id)).get();
}
// load(Object environment, String[] levels) - loads specified levels
// delete()
// getErrorMessages()
// findBySignature(String)
public String toString() {
return getClass().getName()+" "+getId();
}
private Collection errors;
public synchronized Collection getErrors() {
if (errors==null) {
try {
errors=Collections.unmodifiableCollection(repository.getFactory().getEngine().getScanLoadError(dbData.getId(), new ArrayList()));
} catch (SQLException e) {
throw new MesopotamiaRuntimeException("Cannot retrieve load errors: "+e,e);
}
}
return errors;
}
/**
* Shows scan in browser. Use it for debugging.
*/
public void show() {
CompositeVisualizer cv = CompositeVisualizer.getThreadInstance();
cv.addVisualizer(new MesopotamiaVisualizer());
Visualizable v = cv.toVisualizable(this);
final Object monitor = new Object();
Browser browser = new Browser("Scan "+getId(), v.toTree(null)) {
protected void processWindowEvent(WindowEvent e) {
super.processWindowEvent(e);
if (e.getID()==WindowEvent.WINDOW_CLOSED) {
synchronized (monitor) {
monitor.notifyAll();
}
}
}
};
browser.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
browser.setVisible(true);
synchronized (monitor) {
try {
monitor.wait();
} catch (InterruptedException ex) {
// Ignore
}
}
}
}
|