GSoC Projects Source Code Commit:

[STANBOL-1161] - Entity Disambiguation using FOAF Correlation
[STANBOL-1156] - Freebase Entity Disambiguation

git-svn-id: https://svn.apache.org/repos/asf/stanbol/branches/disambiguation@1528907 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/enhancement-engines/foaf-disambiguation/README.md b/enhancement-engines/foaf-disambiguation/README.md
new file mode 100755
index 0000000..642005b
--- /dev/null
+++ b/enhancement-engines/foaf-disambiguation/README.md
@@ -0,0 +1,60 @@
+Stanbol Entity Disambiguation using FOAF Correlation
+======================================================
+
+This is the Stanbol enhacement engine developed as part of the GSoC 2013 project [1]. <br/>
+This engine uses FOAF correlation techniques to disambiguate suggested entities in a content.
+The engine's main functionality is to increase the confidence of Entity-Annotations identified from previous engines, by using 2 fundamental techniques; <br/>
+1. Processing correlating URI references in the Entities to detect connected-ness<br/>
+2. Processing foaf:name comparison with fise:selected-text
+
+Disambiguation Process in Detail
+--------------------------------
+In this project, 2 main algorithms are used as given above and finally the disambiguation-confidence calculated from both algorithms are merged as below; <br/>
+
+original-confidence = oc<br/>
+original-confidence-weight = ocw<br/>
+correlation-disambiguation-score = cds<br/>
+foaf-name-disambigiation-score = fds<br/>
+foaf-name-disambiguation-weight = fdw<br/>
+
+<code>disambiguated-confidence = (oc * ocw) + (cds * cdw) + (fds * fdw)</code>
+
+Here the default weights used are as below;<br/>
+<code>ocw : cdw : fdw = 1 : 2 : 2</code> <br/>
+
+###Correlation based disambiguation
+
+The main objective is to identify correlated URIs between entities and increase the confidence of the most 'connected' entity from the suggested entities. FOAF entities are connected to other similar entities via URI references like foaf:knows, owl:sameAs, rdf:seeAlso. These URI references can be used as correlation factors to cluster related FOAF entities and use it effectively for disambiguation. <br/>
+Not only FOAF, any other types of entities including dbpedia-ont:Person, dbpedia-ont:Organization can be connected with eachother via URI references. In this project, correlation references between entities are used as a main factor for entity disambiguation. All URI/Reference type fields of the entities are extracted and processed to find correlations with other entities suggested. The most connected entity will have the most number of URI correlations.<br/>
+
+The correlation based disambiguation algorithm basically follows below steps;<br/>
+1. Process all entities suggested for the content and extract all unique URIReferences as keys and the entities linked to them as values in a Map. <br/>
+2. For each URI reference, increase the correlation score of entity linked to it, relative to the number of entities linked.<br/>
+3. Calculate the correlation-disambiguation-confidence based on the correlation-score and add it to the total disambiguated confidence. <br/>
+
+
+###FOAF Name based disambiguation
+The second technique used is literal matching of foaf:name field of the entity with the fise:selected-texts in the content. Each entity suggested for the content will be checked for the foaf:name property and it will be matched with the list of selected-texts. With an exact match, the disambiguated-confidence will be increased.<br/> 
+
+Finally the cumulative disambiguated-confidence is calculated based on a weighted scale.<br/>
+
+How to execute the engine
+--------------------------
+This engine requires Entity-Annotations extracted from previous engines, and entityhub pre-configured with FOAF entities. 
+The entityhub-site: <code>foaf-site</code> created by indexing the btc2012 dataset including substantial amount of FOAF data can be found at [2]. <br/>
+Please go through the steps in the project's README to configure the 'foaf-site' in Stanbol entityhub and use it in the foaf-site-chain enhancement-chain. The new disambiguation-foaf engine will be used to extend the functionality of this enhancement-chain in this project.<br/>
+
+After configuring the 'foaf-site' with sufficient a FOAF dataset you can install and use the new engine by following below steps; <br/>
+1. Build the maven project using command : <code>mvn clean install</code> <br/>
+2. Start the Stanbol engine and install the bundle: <code>org.apache.stanbol.enhancer.engines.disambiguation.foaf-1.0-SNAPSHOT.jar</code><br/> 
+3. Configure the foaf-site-chain with the new disambiguation engine
+
+The new engine is identified by : <code>disambiguation-foaf</code>
+Please note that in addition to the foaf-site I have also used entitylinking with dbpedia in the foaf-site-chain to increase the amount of entitiies for disambiguation.
+Therefore after configuring the enhancement-chain successfully the foaf-site-chain should look like below; <br/>
+<pre>
+Engines: langdetect, opennlp-sentence, opennlp-token, opennlp-pos, foaf-site-linking, opennlp-ner, dbpediaLinking, disambiguation-foaf
+</pre>
+
+[1] http://www.google-melange.com/gsoc/proposal/review/google/gsoc2013/dileepaj/1 <br/>
+[2] https://github.com/dileepajayakody/FOAFSite
diff --git a/enhancement-engines/foaf-disambiguation/pom.xml b/enhancement-engines/foaf-disambiguation/pom.xml
new file mode 100755
index 0000000..e619a86
--- /dev/null
+++ b/enhancement-engines/foaf-disambiguation/pom.xml
@@ -0,0 +1,114 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<parent>
+		<groupId>org.apache.stanbol</groupId>
+		<artifactId>apache-stanbol-enhancement-engines</artifactId>
+		<version>0.10.1-SNAPSHOT</version>
+		<!--change the relative path to point to enhancement-engine root pom-->
+		<relativePath>../../apache/stanbol/trunk/enhancement-engines/</relativePath>
+	</parent>
+
+	<groupId>org.apache.stanbol</groupId>
+	<artifactId>org.apache.stanbol.enhancer.engines.disambiguation.foaf</artifactId>
+	<version>1.0-SNAPSHOT</version>
+	<packaging>bundle</packaging>
+
+	<name>foaf-disambiguation</name>
+	<url>http://maven.apache.org</url>
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+	</properties>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.felix</groupId>
+				<artifactId>maven-bundle-plugin</artifactId>
+				<extensions>true</extensions>
+				<configuration>
+					<instructions>
+						<Bundle-DocURL>http://stanbol.apache.org</Bundle-DocURL>
+						<Bundle-Vendor>gsoc-dileepa</Bundle-Vendor>
+						<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+						<Bundle-Version>${project.version}</Bundle-Version>
+						<Export-Package>
+							org.apache.stanbol.enhancer.engine.disambiguation.foaf.*;version=${project.version}
+						</Export-Package>
+						<Import-Package>
+							org.apache.stanbol.enhancer.servicesapi; provide:=true; version="[0.10,0.12)",
+							*;resolution:="optional"
+            			</Import-Package>
+					</instructions>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.felix</groupId>
+				<artifactId>maven-scr-plugin</artifactId>
+			</plugin>
+		</plugins>
+	</build>
+
+	<dependencies>
+		<!--dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> 
+			<version>3.8.1</version> <scope>test</scope> </dependency -->
+
+		<dependency>
+			<groupId>org.apache.stanbol</groupId>
+			<artifactId>org.apache.stanbol.commons.namespaceprefix.service</artifactId>
+			<version>0.11.0</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.stanbol</groupId>
+			<artifactId>org.apache.stanbol.enhancer.engines.entitylinking.engine</artifactId>
+			<version>0.10.1-SNAPSHOT</version>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.stanbol</groupId>
+			<artifactId>org.apache.stanbol.commons.stanboltools.offline</artifactId>
+			<version>0.11.0</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.stanbol</groupId>
+			<artifactId>org.apache.stanbol.enhancer.servicesapi</artifactId>
+			<version>0.10.0</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.stanbol</groupId>
+			<artifactId>org.apache.stanbol.enhancer.nlp</artifactId>
+			<version>0.10.0</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.stanbol</groupId>
+			<artifactId>org.apache.stanbol.entityhub.servicesapi</artifactId>
+			<version>0.11.0</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.felix</groupId>
+			<artifactId>org.apache.felix.scr.annotations</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>commons-lang</groupId>
+			<artifactId>commons-lang</artifactId>
+		</dependency>
+		<!-- Test dependencies -->
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>  <!-- used for debug level logging during tests -->
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-log4j12</artifactId>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+</project>
diff --git a/enhancement-engines/foaf-disambiguation/src/main/java/org/apache/stanbol/enhancer/engine/disambiguation/foaf/EntityAnnotation.java b/enhancement-engines/foaf-disambiguation/src/main/java/org/apache/stanbol/enhancer/engine/disambiguation/foaf/EntityAnnotation.java
new file mode 100755
index 0000000..e39e1c0
--- /dev/null
+++ b/enhancement-engines/foaf-disambiguation/src/main/java/org/apache/stanbol/enhancer/engine/disambiguation/foaf/EntityAnnotation.java
@@ -0,0 +1,365 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.stanbol.enhancer.engine.disambiguation.foaf;
+
+import org.apache.stanbol.enhancer.servicesapi.rdf.Properties;
+
+import java.util.SortedMap;
+import java.util.SortedSet;
+
+import org.apache.clerezza.rdf.core.LiteralFactory;
+import org.apache.clerezza.rdf.core.TripleCollection;
+import org.apache.clerezza.rdf.core.UriRef;
+import org.apache.stanbol.enhancer.servicesapi.ContentItem;
+import org.apache.stanbol.enhancer.servicesapi.helper.EnhancementEngineHelper;
+import org.apache.stanbol.enhancer.servicesapi.rdf.Properties;
+import org.apache.stanbol.entityhub.servicesapi.model.Entity;
+import org.apache.stanbol.entityhub.servicesapi.model.Text;
+import org.apache.stanbol.entityhub.servicesapi.model.rdf.RdfResourceEnum;
+import org.apache.stanbol.entityhub.servicesapi.site.Site;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An abstraction of an EntityAnnotation
+ * 
+ * @author Dileepa Jayakody
+ */
+public class EntityAnnotation implements Comparable<EntityAnnotation> {
+
+	private static final Logger log = LoggerFactory
+			.getLogger(EntityAnnotation.class);
+
+	/**
+	 * foaf:name disambiguation ratio (2.0)
+	 */
+	public static final double FOAFNAME_DISAMBIGUATION_RATIO = 2.0;
+	/**
+	 * URI Reference correlation disambiguation ratio (2.0)
+	 */
+	public static final double URI_CORRELATION_DISAMBIGUATION_RATIO = 2.0;
+	/**
+	 * Default ratio for the original fise:confidence of suggested entities
+	 */
+	public static final double ORIGINAL_CONFIDNECE_RATIO = 1.0;
+
+	/**
+	 * The weight for foaf:name disambiguation scores
+	 */
+	private double foafNameDisambiguationWeight = FOAFNAME_DISAMBIGUATION_RATIO
+			/ (FOAFNAME_DISAMBIGUATION_RATIO + ORIGINAL_CONFIDNECE_RATIO + URI_CORRELATION_DISAMBIGUATION_RATIO);
+	/**
+	 * The weight for uri-correlation disambiguation scores
+	 */
+	private double uriCorrelationDisambiguationWeight = URI_CORRELATION_DISAMBIGUATION_RATIO
+			/ (FOAFNAME_DISAMBIGUATION_RATIO + ORIGINAL_CONFIDNECE_RATIO + URI_CORRELATION_DISAMBIGUATION_RATIO);
+	/**
+	 * The weight for the original confidence scores
+	 * 
+	 */
+	private double confidenceWeight = ORIGINAL_CONFIDNECE_RATIO
+			/ (FOAFNAME_DISAMBIGUATION_RATIO + ORIGINAL_CONFIDNECE_RATIO + URI_CORRELATION_DISAMBIGUATION_RATIO);
+
+	private static final LiteralFactory lf = LiteralFactory.getInstance();
+
+	private static final UriRef ENTITYHUB_SITE = new UriRef(
+			RdfResourceEnum.site.getUri());
+
+	private UriRef uriLink;
+	private UriRef entityUri;
+	private Entity entity;
+
+	private Double originalConfidence = 0.0;
+	// private Double entityReferenceDisambiguationScore = 0.0;
+	private Double foafNameDisambiguationScore = 0.0;
+	private Double disambiguatedConfidence = 0.0;
+	private Double entityReferenceDisambiguatedConfidence = 0.0;
+	private Double foafNameDisambiguatedConfidence = 0.0;
+	// the score assigned based on the number of uri correlations with other
+	// entities
+	private int correlationScore;
+	// uri-references from this entity
+	private int referencesFromEntity;
+	private String site;
+	private String entityType;
+	private String entityLabel;
+
+	private EntityAnnotation(UriRef entityAnnotation) {
+		this.uriLink = entityAnnotation;
+	}
+
+	public EntityAnnotation(Entity entity) {
+		this.entity = entity;
+		this.entityUri = new UriRef(entity.getId());
+		this.site = entity.getSite();
+	}
+
+	/**
+	 * Allows to create EntityAnnotations from existing fise:TextAnnotation
+	 * contained in the metadata of the processed {@link ContentItem}
+	 * 
+	 * @param graph
+	 * @param uri
+	 * @return EntityAnnotation
+	 */
+	public static EntityAnnotation createFromUri(TripleCollection graph,
+			UriRef uri) {
+		EntityAnnotation entityAnnotation = new EntityAnnotation(uri);
+		entityAnnotation.entityUri = EnhancementEngineHelper.getReference(
+				graph, uri, Properties.ENHANCER_ENTITY_REFERENCE);
+		if (entityAnnotation.entityUri == null) {
+			// most likely not a fise:EntityAnnotation
+			log.debug("Unable to create Suggestion for EntityAnnotation {} "
+					+ "because property {} is not present", uri,
+					Properties.ENHANCER_ENTITY_REFERENCE);
+			return null;
+		}
+		entityAnnotation.originalConfidence = EnhancementEngineHelper.get(
+				graph, uri, Properties.ENHANCER_CONFIDENCE, Double.class, lf);
+		if (entityAnnotation.originalConfidence == null) {
+			log.warn("EntityAnnotation {} does not define a value for "
+					+ "property {}. Will use '0' as fallback", uri,
+					Properties.ENHANCER_CONFIDENCE);
+			entityAnnotation.originalConfidence = 0.0;
+		}
+		entityAnnotation.site = EnhancementEngineHelper.getString(graph, uri,
+				ENTITYHUB_SITE);
+		entityAnnotation.entityType = EnhancementEngineHelper.getString(graph,
+				uri, Properties.ENHANCER_ENTITY_TYPE);
+		entityAnnotation.entityLabel = EnhancementEngineHelper.getString(graph,
+				uri, Properties.ENHANCER_ENTITY_LABEL);
+		return entityAnnotation;
+	}
+
+	public void calculateDisambiguatedConfidence() {
+		this.disambiguatedConfidence = (originalConfidence * confidenceWeight)
+				+ this.foafNameDisambiguatedConfidence
+				+ this.entityReferenceDisambiguatedConfidence;
+	}
+
+	public void calculateFoafNameDisambiguatedConfidence() {
+		this.foafNameDisambiguatedConfidence = (foafNameDisambiguationScore * foafNameDisambiguationWeight);
+	}
+
+	/**
+	 * Calculates the disambiguation score obtained for entity's URIReference
+	 * correlations. The score is normalized between [0..1]
+	 * 
+	 * @param maximum
+	 *            correlation score of entities int max
+	 * @param minimum
+	 *            correlation score of entities int min
+	 */
+	public void calculateEntityReferenceDisambiguatedConfidence(int max, int min) {
+		if ((max - min) > 0) {
+			double normalizedCorrelationScore = (correlationScore - min)
+					/ (max - min);
+			this.entityReferenceDisambiguatedConfidence = (normalizedCorrelationScore * uriCorrelationDisambiguationWeight);
+		}
+	}
+
+	/**
+	 * The URI of the fise:EntityAnnotation representing this suggestion in the
+	 * {@link ContentItem#getMetadata() metadata} of the processed
+	 * {@link ContentItem}. This will be <code>null</code>
+	 * 
+	 * @return the URI of the fise:EntityAnnotation or <code>null</code> if not
+	 *         present.
+	 */
+	public UriRef getUriLink() {
+		return uriLink;
+	}
+
+	/**
+	 * Allows to set the URI of the fise:EntityAnnotation. This is required if
+	 * the original enhancement structure shared one fise:EntityAnnotation
+	 * instance for two fise:TextAnnotations (e.g. because both TextAnnotations
+	 * had the exact same value for fise:selected-text). After disambiguation it
+	 * is necessary to 'clone' fise:EntityAnnotations like that to give them
+	 * different fise:confidence values. Because of that it is supported to set
+	 * the new URI of the cloned fise:EntityAnnotation.
+	 * 
+	 * @param uri
+	 *            the uri of the cloned fise:EntityAnnotation
+	 */
+	public void setEntityAnnotation(UriRef uri) {
+		this.uriLink = uri;
+	}
+
+	/**
+	 * The URI of the Entity (MUST NOT be <code>null</code>)
+	 * 
+	 * @return the URI
+	 */
+	public UriRef getEntityUri() {
+		return entityUri;
+	}
+
+	/**
+	 * The original confidence of the fise:EntityAnnotation or <code>null</code>
+	 * if not available.
+	 * 
+	 * @return
+	 */
+	public Double getOriginalConfidnece() {
+		return originalConfidence;
+	}
+
+	/**
+	 * The {@link Entity} or <code>null</code> if not available. For Suggestions
+	 * that are created based on fise:EntityAnnotations the Entity is not
+	 * available. Entities might be loaded as part of the Disambiguation
+	 * process.
+	 * 
+	 * @return the {@link Entity} or <code>null</code> if not available
+	 */
+	public Entity getEntity() {
+		return entity;
+	}
+
+	/**
+	 * The confidence after disambiguation. Will be <code>null</code> at the
+	 * beginning
+	 * 
+	 * @return the disambiguated confidence or <code>null</code> if not yet
+	 *         disambiguated
+	 */
+	public Double getDisambiguatedConfidence() {
+		return disambiguatedConfidence;
+	}
+
+	/**
+	 * The name of the Entityhub {@link Site} the suggested Entity is managed.
+	 * 
+	 * @return the name of the Entityhub {@link Site}
+	 */
+	public String getSite() {
+		return site;
+	}
+
+	public void setEntityType(String entityType) {
+		this.entityType = entityType;
+	}
+
+	public String getEntityType() {
+		return entityType;
+	}
+
+	public void setEntityLabel(String entityLabel) {
+		this.entityLabel = entityLabel;
+	}
+
+	public String getEntityLabel() {
+		return entityLabel;
+	}
+
+	public void setCorrelationScore(int correlationScore) {
+		this.correlationScore = correlationScore;
+	}
+
+	/**
+	 * Setter for the confidence after disambiguation
+	 * 
+	 * @param disambiguatedConfidence
+	 */
+	public void setDisambiguatedConfidence(Double disambiguatedConfidence) {
+		this.disambiguatedConfidence = disambiguatedConfidence;
+	}
+
+	public void increaseCorrelationScore(int corefEntities) {
+		this.correlationScore += corefEntities;
+	}
+
+	public int getCorrelationScore() {
+		return correlationScore;
+	}
+
+	public void setReferencesFromEntity(int linksFromEntity) {
+		this.referencesFromEntity = linksFromEntity;
+	}
+
+	public int getReferencesFromEntity() {
+		return referencesFromEntity;
+	}
+
+	public void setFoafNameDisambiguationScore(
+			Double foafNameDisambiguationScore) {
+		this.foafNameDisambiguationScore = foafNameDisambiguationScore;
+	}
+
+	public Double getFoafNameDisambiguationScore() {
+		return foafNameDisambiguationScore;
+	}
+
+	public void setEntityReferenceDisambiguatedConfidence(
+			Double entityReferenceDisambiguatedConfidence) {
+		this.entityReferenceDisambiguatedConfidence = entityReferenceDisambiguatedConfidence;
+	}
+
+	public Double getEntityReferenceDisambiguatedConfidence() {
+		return entityReferenceDisambiguatedConfidence;
+	}
+
+	public void setFoafNameDisambiguatedConfidence(
+			Double foafNameDisambiguatedConfidence) {
+		this.foafNameDisambiguatedConfidence = foafNameDisambiguatedConfidence;
+	}
+
+	public Double getFoafNameDisambiguatedConfidence() {
+		return foafNameDisambiguatedConfidence;
+	}
+
+	@Override
+	public int hashCode() {
+		return entityUri.hashCode();
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		return obj instanceof EntityAnnotation
+				&& ((EntityAnnotation) obj).entityUri.equals(entityUri);
+	}
+
+	/**
+	 * Compares based on the {@link #getDisambiguatedConfidence()} (if present)
+	 * and falls back to the {@link #getOriginalConfidnece()}. If the original
+	 * confidence value is not present or both Suggestions do have the same
+	 * confidence the natural order of the Entities URI is used. This also
+	 * ensures <code>(x.compareTo(y)==0) == (x.equals(y))</code> and allows to
+	 * use this class with {@link SortedMap} and {@link SortedSet}
+	 * implementations.
+	 * <p>
+	 */
+	@Override
+	public int compareTo(EntityAnnotation other) {
+		int result;
+		if (disambiguatedConfidence != null
+				&& other.disambiguatedConfidence != null) {
+			result = other.disambiguatedConfidence
+					.compareTo(disambiguatedConfidence);
+		} else if (other.originalConfidence != null
+				&& originalConfidence != null) {
+			result = other.originalConfidence.compareTo(originalConfidence);
+		} else {
+			result = 0;
+		}
+		// ensure (x.compareTo(y)==0) == (x.equals(y))
+		return result == 0 ? entityUri.getUnicodeString().compareTo(
+				other.entityUri.getUnicodeString()) : result;
+	}
+
+}
diff --git a/enhancement-engines/foaf-disambiguation/src/main/java/org/apache/stanbol/enhancer/engine/disambiguation/foaf/FOAFDisambiguationEngine.java b/enhancement-engines/foaf-disambiguation/src/main/java/org/apache/stanbol/enhancer/engine/disambiguation/foaf/FOAFDisambiguationEngine.java
new file mode 100755
index 0000000..231d3ba
--- /dev/null
+++ b/enhancement-engines/foaf-disambiguation/src/main/java/org/apache/stanbol/enhancer/engine/disambiguation/foaf/FOAFDisambiguationEngine.java
@@ -0,0 +1,381 @@
+package org.apache.stanbol.enhancer.engine.disambiguation.foaf;
+
+import static org.apache.stanbol.enhancer.servicesapi.rdf.Properties.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.apache.clerezza.rdf.core.Literal;
+import org.apache.clerezza.rdf.core.LiteralFactory;
+import org.apache.clerezza.rdf.core.MGraph;
+import org.apache.clerezza.rdf.core.Triple;
+import org.apache.clerezza.rdf.core.UriRef;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.stanbol.commons.namespaceprefix.NamespacePrefixService;
+import org.apache.stanbol.enhancer.nlp.model.AnalysedText;
+import org.apache.stanbol.enhancer.nlp.utils.NlpEngineHelper;
+import org.apache.stanbol.enhancer.servicesapi.ContentItem;
+import org.apache.stanbol.enhancer.servicesapi.EngineException;
+import org.apache.stanbol.enhancer.servicesapi.EnhancementEngine;
+import org.apache.stanbol.enhancer.servicesapi.InvalidContentException;
+import org.apache.stanbol.enhancer.servicesapi.ServiceProperties;
+import org.apache.stanbol.enhancer.servicesapi.helper.ContentItemHelper;
+import org.apache.stanbol.enhancer.servicesapi.helper.EnhancementEngineHelper;
+import org.apache.stanbol.enhancer.servicesapi.impl.AbstractEnhancementEngine;
+import org.apache.stanbol.enhancer.servicesapi.rdf.TechnicalClasses;
+import org.apache.stanbol.entityhub.servicesapi.model.Entity;
+import org.apache.stanbol.entityhub.servicesapi.model.Representation;
+import org.apache.stanbol.entityhub.servicesapi.model.Text;
+import org.apache.stanbol.entityhub.servicesapi.site.SiteException;
+import org.apache.stanbol.entityhub.servicesapi.site.SiteManager;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The FOAF Disambiguation Engine analyses the connected-ness of the entities
+ * suggested in a content item by identifying correlated URI references of the
+ * entities. The fise:confidence of the entities are increased with the number
+ * of matches of references with other entities.
+ * 
+ * 
+ * @author Dileepa Jayakody
+ * 
+ */
+@Component(immediate = true, metatype = true)
+@Service
+@Properties(value = { @Property(name = EnhancementEngine.PROPERTY_NAME, value = "disambiguation-foaf") })
+public class FOAFDisambiguationEngine extends
+		AbstractEnhancementEngine<IOException, RuntimeException> implements
+		EnhancementEngine, ServiceProperties {
+
+	private static Logger log = LoggerFactory
+			.getLogger(FOAFDisambiguationEngine.class);
+
+	/**
+	 * The default value for the execution of this Engine. Currently set to
+	 * {@link ServiceProperties#ORDERING_POST_PROCESSING} + 90.
+	 * <p>
+	 * This should ensure that this engines runs as one of the first engines of
+	 * the post-processing phase
+	 */
+	public static final Integer defaultOrder = ServiceProperties.ORDERING_POST_PROCESSING - 90;
+
+	/**
+	 * The {@link LiteralFactory} used to create typed RDF literals
+	 */
+	private final LiteralFactory literalFactory = LiteralFactory.getInstance();
+
+	@Reference
+	protected SiteManager siteManager;
+
+	@Reference
+	protected NamespacePrefixService namespacePrefixService;
+
+	// all the URIReferences of entities and the entities which are linked to
+	// those URIreferences
+	// key: URIReference value: Set<EntityAnnotation>
+	private Map<String, Set<UriRef>> urisReferencedByEntities = new HashMap<String, Set<UriRef>>();
+	// all entity annotations suggested for the content
+	private Map<UriRef, EntityAnnotation> allEnitityAnnotations = new HashMap<UriRef, EntityAnnotation>();
+	//correlation scores extracted from URIReference correlations of the suggested entities
+	private SortedSet<Integer> correlationScoresOfEntities = new TreeSet<Integer>();
+	private String FOAF_NAMESPACE;
+
+	@Override
+	public Map<String, Object> getServiceProperties() {
+		return Collections.unmodifiableMap(Collections.singletonMap(
+				ENHANCEMENT_ENGINE_ORDERING, (Object) defaultOrder));
+	}
+
+	@Override
+	public int canEnhance(ContentItem ci) throws EngineException {
+		// check if content is present
+		try {
+			if ((ContentItemHelper.getText(ci.getBlob()) == null)
+					|| (ContentItemHelper.getText(ci.getBlob()).trim()
+							.isEmpty())) {
+				return CANNOT_ENHANCE;
+			}
+		} catch (IOException e) {
+			log.error("Failed to get the text for "
+					+ "enhancement of content: " + ci.getUri(), e);
+			throw new InvalidContentException(this, ci, e);
+		}
+		// default enhancement is synchronous enhancement
+		return ENHANCE_SYNCHRONOUS;
+	}
+
+	@Override
+	public void computeEnhancements(ContentItem ci) throws EngineException {
+		MGraph graph = ci.getMetadata();
+		FOAF_NAMESPACE = namespacePrefixService.getNamespace("foaf");
+		Iterator<Triple> it = graph.filter(null, RDF_TYPE,
+				TechnicalClasses.ENHANCER_TEXTANNOTATION);
+		while (it.hasNext()) {
+			UriRef textAnnotation = (UriRef) it.next().getSubject();
+			// NOTE: this iterator will also include dc:relation between
+			// fise:TextAnnotation's
+			Iterator<Triple> relatedLinks = graph.filter(null, DC_RELATION,
+					textAnnotation);
+			// extracting selected text for foaf-name comparison
+			Iterator<Triple> selectedTextsItr = graph.filter(textAnnotation,
+					ENHANCER_SELECTED_TEXT, null);
+			while (relatedLinks.hasNext()) {
+				UriRef link = (UriRef) relatedLinks.next().getSubject();
+				EntityAnnotation suggestion = EntityAnnotation.createFromUri(
+						graph, link);
+				// if returned suggestion is an entity-annotation proceed with
+				// disambiguation process
+				if (suggestion != null) {
+					// process entityAnnotation for disambiguation
+					try {
+						// process co-referenced entity-references
+						processEntityReferences(suggestion);
+						// matching with foaf:name
+						processFOAFNameDisambiguation(suggestion,
+								selectedTextsItr);
+						// adding new entity annotation to the global map
+						allEnitityAnnotations.put(suggestion.getEntityUri(),
+								suggestion);
+					} catch (SiteException e) {
+						log.error("Error occured while processing entity-annotations : \n"
+								+ e.getMessage());
+						e.printStackTrace();
+					}
+				}
+			}
+		}
+		// calculate correlation scores for entities and disambiguate
+		caculateURICorrelationScoreForEntities();
+		disambiguateEntityReferences();
+		// writing back to graph
+		ci.getLock().writeLock().lock();
+		try {
+			applyDisambiguationResults(graph);
+		} finally {
+			ci.getLock().writeLock().unlock();
+		}
+		clearEhancementData();
+	}
+
+	public void clearEhancementData() {
+		urisReferencedByEntities.clear();
+		allEnitityAnnotations.clear();
+	}
+
+	public Entity getEntityFromEntityHub(EntityAnnotation sug)
+			throws SiteException {
+		UriRef entityUri = sug.getEntityUri();
+		String entityhubSite = sug.getSite();
+		Entity entity = null;
+		// dereferencing the entity from the entityhub
+		if (entityhubSite != null && entityUri != null) {
+			entity = siteManager.getSite(entityhubSite).getEntity(
+					entityUri.getUnicodeString());
+		}
+		return entity;
+	}
+
+	/**
+	 * <p>
+	 * Validates the foaf:name of the entity with the selected text from the
+	 * content, if matched the confidence of the EntityAnnotation is increased.
+	 * </p>
+	 * 
+	 * @param EntityAnnotation
+	 *            ea
+	 * @param The
+	 *            fise:selected-text tokens of the content selectedTextsTriples
+	 * @throws SiteException
+	 */
+	public void processFOAFNameDisambiguation(EntityAnnotation ea,
+			Iterator<Triple> selectedTextsTriples) throws SiteException {
+		Entity entity = this.getEntityFromEntityHub(ea);
+		Representation entityRep = entity.getRepresentation();
+		String foafNameURI = this.FOAF_NAMESPACE + "name";
+		//when comparing selected text with foaf:name, all whitespaces and non-word chars are removed
+		String regexPattern = "[\\s\\W]";
+		Text foafNameText = ((Text) entityRep.getFirst(foafNameURI));
+		if (foafNameText != null) {
+			String foafName = foafNameText.getText();
+			// if the selected-text matches exactly with the foaf-name then
+			// increase the ds by 1
+			Double foafNameScore = 0.0;
+			while (selectedTextsTriples.hasNext()) {
+				String selectedText = ((Literal) selectedTextsTriples.next()
+						.getObject()).getLexicalForm();
+				String selectedTextStr = selectedText.replaceAll(regexPattern, "");
+				if (foafName != null) {
+					String foafNameStr = foafName.replaceAll(regexPattern, "");
+					System.out.println("the regexed foafName:" + foafNameStr);
+					if (selectedTextStr.equalsIgnoreCase(foafNameStr)) {
+						foafNameScore++;
+						break;
+					}
+				}
+
+			}
+			ea.setFoafNameDisambiguationScore(foafNameScore);
+		}
+	}
+
+	/**
+	 * <p>
+	 * Processes all the URIReference type fields of entities and add them to
+	 * the global map as keys and entities as values
+	 * </p>
+	 * 
+	 * @param The
+	 *            EntityAnnotation to process entityAnnotation
+	 * @throws SiteException
+	 */
+	public void processEntityReferences(EntityAnnotation entityAnnotation)
+			throws SiteException {
+		Entity entity = this.getEntityFromEntityHub(entityAnnotation);
+		Representation entityRep = entity.getRepresentation();
+		Iterator<String> fields = entityRep.getFieldNames();
+		int linksFromEntity = 0;
+		while (fields.hasNext()) {
+			String field = fields.next();
+			Iterator<org.apache.stanbol.entityhub.servicesapi.model.Reference> urisReferenced = entityRep
+					.getReferences(field);
+			while (urisReferenced.hasNext()) {
+				org.apache.stanbol.entityhub.servicesapi.model.Reference uriReference = urisReferenced
+						.next();
+				linksFromEntity++;
+				String referenceString = uriReference.getReference();
+				if (urisReferencedByEntities.containsKey(referenceString)) {
+					Set<UriRef> eas = urisReferencedByEntities
+							.get(referenceString);
+					eas.add(entityAnnotation.getEntityUri());
+					urisReferencedByEntities.put(referenceString, eas);
+				} else {
+					Set<UriRef> eas = new HashSet<UriRef>();
+					eas.add(entityAnnotation.getEntityUri());
+					// key:link, value:entityAnnotation set referencing link
+					urisReferencedByEntities.put(referenceString, eas);
+				}
+			}
+		}
+		entityAnnotation.setReferencesFromEntity(linksFromEntity);
+	}
+
+	/**
+	 * <p>
+	 * Counts the number of correlated URI-References and add that score to
+	 * correlated entities
+	 * </p>
+	 */
+	public void caculateURICorrelationScoreForEntities() {
+		for (String uriReference : urisReferencedByEntities.keySet()) {
+			Set<UriRef> entityAnnotationsLinked = urisReferencedByEntities
+					.get(uriReference);
+			int correlationScoreForURI = entityAnnotationsLinked.size();
+			// adding the correlationscore to the global set for normalization
+			// requirements
+			this.correlationScoresOfEntities.add(new Integer(
+					correlationScoreForURI));
+			for (UriRef ea : entityAnnotationsLinked) {
+				if (allEnitityAnnotations.get(ea) != null) {
+					allEnitityAnnotations.get(ea).increaseCorrelationScore(
+							correlationScoreForURI);
+				}
+			}
+		}
+	}
+
+	public void disambiguateEntityReferences() {
+		int allUriRefs = urisReferencedByEntities.keySet().size();
+		for (EntityAnnotation ea : allEnitityAnnotations.values()) {
+			this.performEntityReferenceDisambiguation(ea, allUriRefs);
+		}
+	}
+
+	public void performEntityReferenceDisambiguation(EntityAnnotation ea,
+			int allUriReferences) {
+		int correlationScoreForEntity = ea.getCorrelationScore();
+		int refsFromEntity = ea.getReferencesFromEntity();
+		int correlationsWithOtherEntities = correlationScoreForEntity
+				- refsFromEntity;
+		ea.setCorrelationScore(correlationsWithOtherEntities);
+	}
+
+	public void applyDisambiguationResults(MGraph graph) {
+		int max = this.correlationScoresOfEntities.last();
+		int min = this.correlationScoresOfEntities.first();
+	
+		for (EntityAnnotation ea : allEnitityAnnotations.values()) {
+			// calculate total dc
+			ea.calculateFoafNameDisambiguatedConfidence();
+			ea.calculateEntityReferenceDisambiguatedConfidence(max, min);
+			ea.calculateDisambiguatedConfidence();
+			/*
+			System.out.println("\n\nEntity : " + ea.getEntityLabel()
+					+ "\n site: " + ea.getSite() + "\n originalconf: "
+					+ ea.getOriginalConfidnece().toString()
+					+ "\n no of links from entity: "
+					+ ea.getReferencesFromEntity()
+					+ "\n  entity foafname-score :"
+					+ ea.getFoafNameDisambiguationScore()
+					+ "\n no of matches : " + ea.getCorrelationScore()
+					+ "\n  entity correlation-score :"
+					+ ea.getCorrelationScore() + "\n foaf name disamb-conf: "
+					+ ea.getFoafNameDisambiguatedConfidence().toString()
+					+ "\n entity reference disamb-conf: "
+					+ ea.getEntityReferenceDisambiguatedConfidence().toString()
+					+ "\n Total disamb-conf: "
+					+ ea.getDisambiguatedConfidence().toString());
+*/
+			EnhancementEngineHelper.set(graph, ea.getUriLink(),
+					ENHANCER_CONFIDENCE, ea.getDisambiguatedConfidence(),
+					literalFactory);
+			// adding this engine as a contributor
+			EnhancementEngineHelper.addContributingEngine(graph,
+					ea.getUriLink(), this);
+		}
+	}
+
+	/**
+	 * Activate and read the properties
+	 * 
+	 * @param ce
+	 *            the {@link ComponentContext}
+	 */
+	@Activate
+	protected void activate(ComponentContext ce) throws ConfigurationException {
+		try {
+			super.activate(ce);
+
+		} catch (IOException e) {
+			log.error("Error in activation method.", e);
+		}
+	}
+
+	/**
+	 * Deactivate
+	 * 
+	 * @param ce
+	 *            the {@link ComponentContext}
+	 */
+	@Deactivate
+	protected void deactivate(ComponentContext ce) {
+		super.deactivate(ce);
+	}
+}
diff --git a/enhancement-engines/foaf-disambiguation/src/test/java/org/apache/stanbol/AppTest.java b/enhancement-engines/foaf-disambiguation/src/test/java/org/apache/stanbol/AppTest.java
new file mode 100755
index 0000000..dfd3e87
--- /dev/null
+++ b/enhancement-engines/foaf-disambiguation/src/test/java/org/apache/stanbol/AppTest.java
@@ -0,0 +1,38 @@
+package org.apache.stanbol;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Unit test for simple App.
+ */
+public class AppTest 
+    extends TestCase
+{
+    /**
+     * Create the test case
+     *
+     * @param testName name of the test case
+     */
+    public AppTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * @return the suite of tests being tested
+     */
+    public static Test suite()
+    {
+        return new TestSuite( AppTest.class );
+    }
+
+    /**
+     * Rigourous Test :-)
+     */
+    public void testApp()
+    {
+        assertTrue( true );
+    }
+}
diff --git a/enhancement-engines/freebase-disambiguation/LICENSE b/enhancement-engines/freebase-disambiguation/LICENSE
new file mode 100644
index 0000000..581e5e2
--- /dev/null
+++ b/enhancement-engines/freebase-disambiguation/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. 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 and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!) The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [2013] [Antonio David Perez Morales]
+
+   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.
\ No newline at end of file
diff --git a/enhancement-engines/freebase-disambiguation/README.md b/enhancement-engines/freebase-disambiguation/README.md
new file mode 100644
index 0000000..5296a5a
--- /dev/null
+++ b/enhancement-engines/freebase-disambiguation/README.md
@@ -0,0 +1,53 @@
+# Freebase Disambiguation Engine #
+
+The Freebase Disambiguation Engine is a Stanbol Enhancer Engine responsible of try to disambiguate entities depending on the context of such entities.
+This engine uses an algorithm based on minimum distances between entities in the Freebase Graph (generated using the [Freebase graph importer][1]) 
+
+The disambiguation algorithm should take into account a local disambiguation score (comparing in some way the document context with the contexts provided by Wikilinks resource) and a global disambiguation score computed by a graph based algorithm using the Freebase graph imported in a Neo4j database. Each disambiguation score would have a different weight in the final disambiguation store for each entity. The algorithm's steps, for each TextAnnotation, can be the following:
+
+1. Local score: for each EntityAnnotation, retrieves from Wikilinks database all the contexts associated to the referenced entity. Compare (similarity, distance....) the mention context (selected-context) with the wikilinks contexts.
+
+2. Global score: build a subgraph with all the possible entities and its relations in Freebase. Extract a set of possibles solutions from such graph (note: a solution should include only one entity annotation for each text annotation). Compute the Dijsktra distance between each pair of entities belonging to a possible solution.
+
+3. Weights normalization and confidence values refinement. 
+
+## Freebase Stanbol Enhancer Engine ##
+
+This engine implements the above algorithm but the first point (local score) which is not implemented in this version.
+
+The algorithm builds a subgraph from the whole Freebase graph only for the entities returned after the NLP and Entity linking process, and the relations between them.
+
+Using the Entity Annotations for each Text Annotation, it builds all the possible solutions for the text to enhance. It means, all the possible tuples result of combining the entities in each set of entity annotations (for each text annotation).
+
+The searched solution is the tuple minimizing the distance in the graph between every pair of entities in the tuple. Minimal distance means higher disambiguation score.
+
+## How to use it ##
+
+In order to use the engine, do the following:  
+
+1. Download the code
+2. Run `'mvn clean package'` command
+3. In the *target/* directory, find the bundle called `gsoc-freebase-disambiguation-{version}-jar`
+4. Install it in Stanbol using the Felix Web Console
+
+**Note:** This bundle depends on blueprints-core` and `blueprints-neo4j-graph`. You have to download the source code from [Blueprints repository][2] and use the pom files located in *src/main/resources* folder of this project to convert them into bundles and install them in Stanbol
+
+## Configuration ##
+
+Once the bundle is deployed and active in Stanbol, go to configuration tab in Felix Web Console of Stanbol and configure the *FreebaseDisambiguatprEngine*:
+* Name of the engine: default value is **freebase-disambiguation**
+* Neo4j graph location: default value is empty. You must set the location of the graph and restart the component in the *Component* tab.
+
+The last step is configure a new engine in the enhancement chain using the name set in the configuration (freebase-disambiguator).
+
+## Jira ##
+
+This tool is related to the [issue 1157](https://issues.apache.org/jira/browse/STANBOL-1157) of Stanbol Jira.  
+
+## License
+
+GSoC Freebase Disambiguation Engine is distributed under the terms of the [Apache License, 2.0](http://www.apache.org/licenses/LICENSE-2.0.html).
+
+[1]: https://github.com/adperezmorales/gsoc-freebase-graph-importer/tree/master/gsoc-freebase-graph-importer
+[2]: https://github.com/tinkerpop/blueprints
+
diff --git a/enhancement-engines/freebase-disambiguation/pom.xml b/enhancement-engines/freebase-disambiguation/pom.xml
new file mode 100644
index 0000000..6187c3a
--- /dev/null
+++ b/enhancement-engines/freebase-disambiguation/pom.xml
@@ -0,0 +1,221 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>com.gsoc.freebase</groupId>
+	<artifactId>gsoc-freebase-disambiguation-engine</artifactId>
+	<version>1.0-SNAPSHOT</version>
+	<name>Freebase Disambiguation Engine</name>
+	<description>Freebase Disambiguation Engine which makes use of the Graph generated by the gsoc-freebase-importer to disambiguate entities in the Stanbol Enhancer Chain</description>
+
+	<!-- Developers -->
+	<developers>
+		<developer>
+			<name>Antonio David Perez Morales</name>
+			<email>adperezmorales@gmail.com</email>
+			<id>adpm</id>
+		</developer>
+	</developers>
+
+	<!-- Properties -->
+	<properties>
+		<stanbol.version>0.10.0</stanbol.version>
+		<stanbol.snapshotversion>0.10.0-SNAPSHOT</stanbol.snapshotversion>
+		<felix.version>1.6.0</felix.version>
+		<blueprints.version>2.3.0</blueprints.version>
+	</properties>
+
+	<!-- Dependencies -->
+	<dependencies>
+		<!-- Stanbol dependencies -->
+		<dependency>
+			<groupId>org.apache.stanbol</groupId>
+			<artifactId>org.apache.stanbol.enhancer.servicesapi</artifactId>
+			<version>${stanbol.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.stanbol</groupId>
+			<artifactId>org.apache.stanbol.enhancer.test</artifactId>
+			<version>${stanbol.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.stanbol</groupId>
+			<artifactId>org.apache.stanbol.enhancer.engine.disambiguation.mlt</artifactId>
+			<version>${stanbol.snapshotversion}</version>
+			<scope>provided</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.felix</groupId>
+			<artifactId>org.apache.felix.scr.annotations</artifactId>
+			<version>${felix.version}</version>
+			<scope>provided</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>com.tinkerpop.blueprints</groupId>
+			<artifactId>blueprints-core</artifactId>
+			<version>2.3.0</version>
+			<scope>compile</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>com.tinkerpop.blueprints</groupId>
+			<artifactId>blueprints-neo4j-graph</artifactId>
+			<version>${blueprints.version}</version>
+			<scope>compile</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>com.tinkerpop.blueprints</groupId>
+			<artifactId>blueprints-graph-jung</artifactId>
+			<version>${blueprints.version}</version>
+			<scope>compile</scope>
+		</dependency>
+
+		<!-- Guava Dependency -->
+		<dependency>
+			<groupId>com.google.guava</groupId>
+			<artifactId>guava</artifactId>
+			<version>14.0.1</version>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugin</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<version>2.4</version>
+				<configuration>
+					<archive>
+						<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+					</archive>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.felix</groupId>
+				<artifactId>maven-bundle-plugin</artifactId>
+				<extensions>true</extensions>
+				<configuration>
+					<instructions>
+						<!-- Enable this for including your --> <!-- enhancement chain configuration --> <!-- <Install-Path>config</Install-Path> -->
+						<Export-Package>
+							org.apache.stanbol.enhancer.engine.disambiguation.freebase*;version=${project.version}
+						</Export-Package>
+						<Import-Package>com.google.common.collect;version="13.0.1",
+							*;resolution:="optional"</Import-Package>
+						<!-- <Embed-Dependency>*;scope=compile;inline=true;artifactId=!neo4j|!org.codehaus|!org.apache.stanbol.enhancer.engine.disambiguation.mlt</Embed-Dependency> 
+							<Embed-Directory>lib/</Embed-Directory> <Embed-Transitive>true</Embed-Transitive> 
+							<Include-Resource>{maven-dependencies}</Include-Resource> -->
+					</instructions>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.felix</groupId>
+				<artifactId>maven-scr-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>generate-scr-scrdescriptor</id>
+						<goals>
+							<goal>scr</goal>
+						</goals>
+						<configuration>
+							<properties>
+								<service.vendor>gsoc-freebase-disambiguation-engine</service.vendor>
+							</properties>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+
+		<!-- Plugins Management -->
+		<pluginManagement>
+			<plugins>
+				<plugin>
+					<groupId>org.apache.felix</groupId>
+					<artifactId>maven-bundle-plugin</artifactId>
+					<version>2.3.7</version>
+					<inherited>true</inherited>
+					<configuration>
+						<archive>
+							<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+						</archive>
+						<instructions>
+							<Bundle-DocURL>http://stanbol.apache.org</Bundle-DocURL>
+							<Bundle-Vendor>gsoc</Bundle-Vendor>
+							<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+							<_versionpolicy>$${version;===;${@}}</_versionpolicy>
+						</instructions>
+					</configuration>
+					<executions>
+						<execution>
+							<id>bundle-manifest</id>
+							<phase>process-classes</phase>
+							<goals>
+								<goal>manifest</goal>
+							</goals>
+						</execution>
+						<execution>
+							<id>bundle-bundle</id>
+							<phase>package</phase>
+							<goals>
+								<goal>bundle</goal>
+							</goals>
+						</execution>
+					</executions>
+				</plugin>
+				<plugin>
+					<groupId>org.apache.felix</groupId>
+					<artifactId>maven-scr-plugin</artifactId>
+					<version>1.7.4</version>
+					<executions>
+						<execution>
+							<id>generate-scr-scrdescriptor</id>
+							<goals>
+								<goal>scr</goal>
+							</goals>
+							<configuration>
+								<properties>
+									<service.vendor>gsoc-freebase-disambiguation-engine</service.vendor>
+								</properties>
+							</configuration>
+						</execution>
+					</executions>
+				</plugin>
+
+				<!--This plugin's configuration is used to store Eclipse m2e settings 
+					only. It has no influence on the Maven build itself. -->
+				<plugin>
+					<groupId>org.eclipse.m2e</groupId>
+					<artifactId>lifecycle-mapping</artifactId>
+					<version>1.0.0</version>
+					<configuration>
+						<lifecycleMappingMetadata>
+							<pluginExecutions>
+								<pluginExecution>
+									<pluginExecutionFilter>
+										<groupId>org.apache.felix</groupId>
+										<artifactId>
+											maven-scr-plugin
+										</artifactId>
+										<versionRange>
+											[1.7.4,)
+										</versionRange>
+										<goals>
+											<goal>scr</goal>
+										</goals>
+									</pluginExecutionFilter>
+									<action>
+										<ignore></ignore>
+									</action>
+								</pluginExecution>
+							</pluginExecutions>
+						</lifecycleMappingMetadata>
+					</configuration>
+				</plugin>
+			</plugins>
+		</pluginManagement>
+		<!-- End Plugins Management -->
+	</build>
+</project>
\ No newline at end of file
diff --git a/enhancement-engines/freebase-disambiguation/src/main/java/org/apache/stanbol/enhancer/engine/disambiguation/freebase/graph/CustomDijkstraDistance.java b/enhancement-engines/freebase-disambiguation/src/main/java/org/apache/stanbol/enhancer/engine/disambiguation/freebase/graph/CustomDijkstraDistance.java
new file mode 100644
index 0000000..864ccf7
--- /dev/null
+++ b/enhancement-engines/freebase-disambiguation/src/main/java/org/apache/stanbol/enhancer/engine/disambiguation/freebase/graph/CustomDijkstraDistance.java
@@ -0,0 +1,524 @@
+package org.apache.stanbol.enhancer.engine.disambiguation.freebase.graph;
+
+/*
+ * Created on Jul 9, 2005
+ *
+ * Copyright (c) 2005, the JUNG Project and the Regents of the University 
+ * of California
+ * All rights reserved.
+ *
+ * This software is open-source under the BSD license; see either
+ * "license.txt" or
+ * http://jung.sourceforge.net/license.txt for a description.
+ */
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.collections15.FunctorException;
+
+import edu.uci.ics.jung.algorithms.shortestpath.Distance;
+import edu.uci.ics.jung.algorithms.util.BasicMapEntry;
+import edu.uci.ics.jung.algorithms.util.MapBinaryHeap;
+import edu.uci.ics.jung.graph.Graph;
+import edu.uci.ics.jung.graph.Hypergraph;
+
+/**
+ * <p>
+ * Calculates distances in a specified graph, using Dijkstra's single-source-shortest-path algorithm. All edge
+ * weights in the graph must be nonnegative; if any edge with negative weight is found in the course of
+ * calculating distances, an <code>IllegalArgumentException</code> will be thrown. (Note: this exception will
+ * only be thrown when such an edge would be used to update a given tentative distance; the algorithm does not
+ * check for negative-weight edges "up front".)
+ * 
+ * <p>
+ * Distances and partial results are optionally cached (by this instance) for later reference. Thus, if the 10
+ * closest vertices to a specified source vertex are known, calculating the 20 closest vertices does not
+ * require starting Dijkstra's algorithm over from scratch.
+ * </p>
+ * 
+ * <p>
+ * Distances are stored as double-precision values. If a vertex is not reachable from the specified source
+ * vertex, no distance is stored. <b>This is new behavior with version 1.4</b>; the previous behavior was to
+ * store a value of <code>Double.POSITIVE_INFINITY</code>. This change gives the algorithm an approximate
+ * complexity of O(kD log k), where k is either the number of requested targets or the number of reachable
+ * vertices (whichever is smaller), and D is the average degree of a vertex.
+ * </p>
+ * 
+ * <p>
+ * The elements in the maps returned by <code>getDistanceMap</code> are ordered (that is, returned by the
+ * iterator) by nondecreasing distance from <code>source</code>.
+ * </p>
+ * 
+ * <p>
+ * Users are cautioned that distances calculated should be assumed to be invalidated by changes to the graph,
+ * and should invoke <code>reset()</code> when appropriate so that the distances can be recalculated.
+ * </p>
+ * 
+ * @author Joshua O'Madadhain
+ * @author Tom Nelson converted to jung2
+ */
+public class CustomDijkstraDistance<V,E> implements Distance<V> {
+    protected Hypergraph<V,E> g;
+    protected Transformer<E,? extends Number> nev;
+    protected Map<V,SourceData> sourceMap; // a map of source vertices to an instance of SourceData
+    protected boolean cached;
+    protected double max_distance;
+    protected int max_targets;
+
+    /**
+     * <p>
+     * Creates an instance of <code>DijkstraShortestPath</code> for the specified graph and the specified
+     * method of extracting weights from edges, which caches results locally if and only if
+     * <code>cached</code> is <code>true</code>.
+     * 
+     * @param g
+     *            the graph on which distances will be calculated
+     * @param nev
+     *            the class responsible for returning weights for edges
+     * @param cached
+     *            specifies whether the results are to be cached
+     * @return
+     */
+    public CustomDijkstraDistance(Hypergraph<V,E> g, Transformer<E,? extends Number> nev, boolean cached) {
+        this.g = g;
+        this.nev = nev;
+        this.sourceMap = new HashMap<V,SourceData>();
+        this.cached = cached;
+        this.max_distance = Double.POSITIVE_INFINITY;
+        this.max_targets = Integer.MAX_VALUE;
+    }
+
+    /**
+     * <p>
+     * Creates an instance of <code>DijkstraShortestPath</code> for the specified graph and the specified
+     * method of extracting weights from edges, which caches results locally.
+     * 
+     * @param g
+     *            the graph on which distances will be calculated
+     * @param nev
+     *            the class responsible for returning weights for edges
+     */
+    public CustomDijkstraDistance(Hypergraph<V,E> g, Transformer<E,? extends Number> nev) {
+        this(g, nev, true);
+    }
+
+    /**
+     * <p>
+     * Creates an instance of <code>DijkstraShortestPath</code> for the specified unweighted graph (that is,
+     * all weights 1) which caches results locally.
+     * 
+     * @param g
+     *            the graph on which distances will be calculated
+     */
+    @SuppressWarnings("unchecked")
+    public CustomDijkstraDistance(Graph<V,E> g) {
+        this(g, new ConstantTransformer(1), true);
+    }
+
+    /**
+     * <p>
+     * Creates an instance of <code>DijkstraShortestPath</code> for the specified unweighted graph (that is,
+     * all weights 1) which caches results locally.
+     * 
+     * @param g
+     *            the graph on which distances will be calculated
+     * @param cached
+     *            specifies whether the results are to be cached
+     */
+    @SuppressWarnings("unchecked")
+    public CustomDijkstraDistance(Graph<V,E> g, boolean cached) {
+        this(g, new ConstantTransformer(1), cached);
+    }
+
+    /**
+     * Implements Dijkstra's single-source shortest-path algorithm for weighted graphs. Uses a
+     * <code>MapBinaryHeap</code> as the priority queue, which gives this algorithm a time complexity of O(m
+     * lg n) (m = # of edges, n = # of vertices). This algorithm will terminate when any of the following have
+     * occurred (in order of priority):
+     * <ul>
+     * <li>the distance to the specified target (if any) has been found
+     * <li>no more vertices are reachable
+     * <li>the specified # of distances have been found, or the maximum distance desired has been exceeded
+     * <li>all distances have been found
+     * </ul>
+     * 
+     * @param source
+     *            the vertex from which distances are to be measured
+     * @param numDests
+     *            the number of distances to measure
+     * @param targets
+     *            the set of vertices to which distances are to be measured
+     */
+    protected LinkedHashMap<V,Number> singleSourceShortestPath(V source, Collection<V> targets, int numDests) {
+        SourceData sd = getSourceData(source);
+
+        Set<V> to_get = new HashSet<V>();
+        if (targets != null) {
+            to_get.addAll(targets);
+            Set<V> existing_dists = sd.distances.keySet();
+            for (V o : targets) {
+                if (existing_dists.contains(o)) to_get.remove(o);
+            }
+        }
+
+        // if we've exceeded the max distance or max # of distances we're willing to calculate, or
+        // if we already have all the distances we need,
+        // terminate
+        if (sd.reached_max || (targets != null && to_get.isEmpty()) || (sd.distances.size() >= numDests)) {
+            return sd.distances;
+        }
+
+        while (!sd.unknownVertices.isEmpty() && (sd.distances.size() < numDests || !to_get.isEmpty())) {
+            Map.Entry<V,Number> p = sd.getNextVertex();
+            V v = p.getKey();
+            double v_dist = p.getValue().doubleValue();
+            to_get.remove(v);
+            if (v_dist > this.max_distance) {
+                // we're done; put this vertex back in so that we're not including
+                // a distance beyond what we specified
+                sd.restoreVertex(v, v_dist);
+                sd.reached_max = true;
+                break;
+            }
+            sd.dist_reached = v_dist;
+
+            if (sd.distances.size() >= this.max_targets) {
+                sd.reached_max = true;
+                break;
+            }
+
+            for (E e : getEdgesToCheck(v)) {
+                for (V w : g.getIncidentVertices(e)) {
+                    if (!sd.distances.containsKey(w)) {
+                        double edge_weight = nev.transform(e).doubleValue();
+                        if (edge_weight < 0) throw new IllegalArgumentException(
+                                "Edges weights must be non-negative");
+                        double new_dist = v_dist + edge_weight;
+                        if (!sd.estimatedDistances.containsKey(w)) {
+                            sd.createRecord(w, e, new_dist);
+                        } else {
+                            double w_dist = ((Double) sd.estimatedDistances.get(w)).doubleValue();
+                            if (new_dist < w_dist) // update tentative distance & path for w
+                            sd.update(w, e, new_dist);
+                        }
+                    }
+                }
+            }
+        }
+        return sd.distances;
+    }
+
+    protected SourceData getSourceData(V source) {
+        SourceData sd = sourceMap.get(source);
+        if (sd == null) sd = new SourceData(source);
+        return sd;
+    }
+
+    /**
+     * Returns the set of edges incident to <code>v</code> that should be tested. By default, this is the set
+     * of outgoing edges for instances of <code>Graph</code>, the set of incident edges for instances of
+     * <code>Hypergraph</code>, and is otherwise undefined.
+     */
+    protected Collection<E> getEdgesToCheck(V v) {
+        if (g instanceof Graph) return ((Graph<V,E>) g).getOutEdges(v);
+        else return g.getIncidentEdges(v);
+
+    }
+
+    /**
+     * Returns the length of a shortest path from the source to the target vertex, or null if the target is
+     * not reachable from the source. If either vertex is not in the graph for which this instance was
+     * created, throws <code>IllegalArgumentException</code>.
+     * 
+     * @see #getDistanceMap(Object)
+     * @see #getDistanceMap(Object,int)
+     */
+    public Number getDistance(V source, V target) {
+        if (g.containsVertex(target) == false) throw new IllegalArgumentException("Specified target vertex "
+                                                                                  + target
+                                                                                  + " is not part of graph "
+                                                                                  + g);
+        if (g.containsVertex(source) == false) throw new IllegalArgumentException("Specified source vertex "
+                                                                                  + source
+                                                                                  + " is not part of graph "
+                                                                                  + g);
+
+        Set<V> targets = new HashSet<V>();
+        targets.add(target);
+        Map<V,Number> distanceMap = getDistanceMap(source, targets);
+        return distanceMap.get(target);
+    }
+
+    /**
+     * Returns a {@code Map} from each element {@code t} of {@code targets} to the shortest-path distance from
+     * {@code source} to {@code t}.
+     */
+    public Map<V,Number> getDistanceMap(V source, Collection<V> targets) {
+        if (g.containsVertex(source) == false) throw new IllegalArgumentException("Specified source vertex "
+                                                                                  + source
+                                                                                  + " is not part of graph "
+                                                                                  + g);
+        if (targets.size() > max_targets) throw new IllegalArgumentException(
+                "size of target set exceeds maximum " + "number of targets allowed: " + this.max_targets);
+
+        Map<V,Number> distanceMap = singleSourceShortestPath(source, targets,
+            Math.min(g.getVertexCount(), max_targets));
+        if (!cached) reset(source);
+
+        return distanceMap;
+    }
+
+    /**
+     * <p>
+     * Returns a <code>LinkedHashMap</code> which maps each vertex in the graph (including the
+     * <code>source</code> vertex) to its distance from the <code>source</code> vertex. The map's iterator
+     * will return the elements in order of increasing distance from <code>source</code>.
+     * </p>
+     * 
+     * <p>
+     * The size of the map returned will be the number of vertices reachable from <code>source</code>.
+     * </p>
+     * 
+     * @see #getDistanceMap(Object,int)
+     * @see #getDistance(Object,Object)
+     * @param source
+     *            the vertex from which distances are measured
+     */
+    public Map<V,Number> getDistanceMap(V source) {
+        return getDistanceMap(source, Math.min(g.getVertexCount(), max_targets));
+    }
+
+    /**
+     * <p>
+     * Returns a <code>LinkedHashMap</code> which maps each of the closest <code>numDist</code> vertices to
+     * the <code>source</code> vertex in the graph (including the <code>source</code> vertex) to its distance
+     * from the <code>source</code> vertex. Throws an <code>IllegalArgumentException</code> if
+     * <code>source</code> is not in this instance's graph, or if <code>numDests</code> is either less than 1
+     * or greater than the number of vertices in the graph.
+     * </p>
+     * 
+     * <p>
+     * The size of the map returned will be the smaller of <code>numDests</code> and the number of vertices
+     * reachable from <code>source</code>.
+     * 
+     * @see #getDistanceMap(Object)
+     * @see #getDistance(Object,Object)
+     * @param source
+     *            the vertex from which distances are measured
+     * @param numDests
+     *            the number of vertices for which to measure distances
+     */
+    public LinkedHashMap<V,Number> getDistanceMap(V source, int numDests) {
+
+        if (g.getVertices().contains(source) == false) {
+            throw new IllegalArgumentException("Specified source vertex " + source + " is not part of graph "
+                                               + g);
+
+        }
+        if (numDests < 1 || numDests > g.getVertexCount()) throw new IllegalArgumentException(
+                "numDests must be >= 1 " + "and <= g.numVertices()");
+
+        if (numDests > max_targets) throw new IllegalArgumentException("numDests must be <= the maximum "
+                                                                       + "number of targets allowed: "
+                                                                       + this.max_targets);
+
+        LinkedHashMap<V,Number> distanceMap = singleSourceShortestPath(source, null, numDests);
+
+        if (!cached) reset(source);
+
+        return distanceMap;
+    }
+
+    /**
+     * Allows the user to specify the maximum distance that this instance will calculate. Any vertices past
+     * this distance will effectively be unreachable from the source, in the sense that the algorithm will not
+     * calculate the distance to any vertices which are farther away than this distance. A negative value for
+     * <code>max_dist</code> will ensure that no further distances are calculated.
+     * 
+     * <p>
+     * This can be useful for limiting the amount of time and space used by this algorithm if the graph is
+     * very large.
+     * </p>
+     * 
+     * <p>
+     * Note: if this instance has already calculated distances greater than <code>max_dist</code>, and the
+     * results are cached, those results will still be valid and available; this limit applies only to
+     * subsequent distance calculations.
+     * </p>
+     * 
+     * @see #setMaxTargets(int)
+     */
+    public void setMaxDistance(double max_dist) {
+        this.max_distance = max_dist;
+        for (V v : sourceMap.keySet()) {
+            SourceData sd = sourceMap.get(v);
+            sd.reached_max = (this.max_distance <= sd.dist_reached) || (sd.distances.size() >= max_targets);
+        }
+    }
+
+    /**
+     * Allows the user to specify the maximum number of target vertices per source vertex for which this
+     * instance will calculate distances. Once this threshold is reached, any further vertices will
+     * effectively be unreachable from the source, in the sense that the algorithm will not calculate the
+     * distance to any more vertices. A negative value for <code>max_targets</code> will ensure that no
+     * further distances are calculated.
+     * 
+     * <p>
+     * This can be useful for limiting the amount of time and space used by this algorithm if the graph is
+     * very large.
+     * </p>
+     * 
+     * <p>
+     * Note: if this instance has already calculated distances to a greater number of targets than
+     * <code>max_targets</code>, and the results are cached, those results will still be valid and available;
+     * this limit applies only to subsequent distance calculations.
+     * </p>
+     * 
+     * @see #setMaxDistance(double)
+     */
+    public void setMaxTargets(int max_targets) {
+        this.max_targets = max_targets;
+        for (V v : sourceMap.keySet()) {
+            SourceData sd = sourceMap.get(v);
+            sd.reached_max = (this.max_distance <= sd.dist_reached) || (sd.distances.size() >= max_targets);
+        }
+    }
+
+    /**
+     * Clears all stored distances for this instance. Should be called whenever the graph is modified (edge
+     * weights changed or edges added/removed). If the user knows that some currently calculated distances are
+     * unaffected by a change, <code>reset(V)</code> may be appropriate instead.
+     * 
+     * @see #reset(Object)
+     */
+    public void reset() {
+        sourceMap = new HashMap<V,SourceData>();
+    }
+
+    /**
+     * Specifies whether or not this instance of <code>DijkstraShortestPath</code> should cache its results
+     * (final and partial) for future reference.
+     * 
+     * @param enable
+     *            <code>true</code> if the results are to be cached, and <code>false</code> otherwise
+     */
+    public void enableCaching(boolean enable) {
+        this.cached = enable;
+    }
+
+    /**
+     * Clears all stored distances for the specified source vertex <code>source</code>. Should be called
+     * whenever the stored distances from this vertex are invalidated by changes to the graph.
+     * 
+     * @see #reset()
+     */
+    public void reset(V source) {
+        sourceMap.put(source, null);
+    }
+
+    /**
+     * Compares according to distances, so that the BinaryHeap knows how to order the tree.
+     */
+    protected static class VertexComparator<V> implements Comparator<V> {
+        private Map<V,Number> distances;
+
+        protected VertexComparator(Map<V,Number> distances) {
+            this.distances = distances;
+        }
+
+        public int compare(V o1, V o2) {
+            return ((Double) distances.get(o1)).compareTo((Double) distances.get(o2));
+        }
+    }
+
+    /**
+     * For a given source vertex, holds the estimated and final distances, tentative and final assignments of
+     * incoming edges on the shortest path from the source vertex, and a priority queue (ordered by estimated
+     * distance) of the vertices for which distances are unknown.
+     * 
+     * @author Joshua O'Madadhain
+     */
+    protected class SourceData {
+        protected LinkedHashMap<V,Number> distances;
+        protected Map<V,Number> estimatedDistances;
+        protected MapBinaryHeap<V> unknownVertices;
+        protected boolean reached_max = false;
+        protected double dist_reached = 0;
+
+        protected SourceData(V source) {
+            distances = new LinkedHashMap<V,Number>();
+            estimatedDistances = new HashMap<V,Number>();
+            unknownVertices = new MapBinaryHeap<V>(new VertexComparator<V>(estimatedDistances));
+
+            sourceMap.put(source, this);
+
+            // initialize priority queue
+            estimatedDistances.put(source, new Double(0)); // distance from source to itself is 0
+            unknownVertices.add(source);
+            reached_max = false;
+            dist_reached = 0;
+        }
+
+        protected Map.Entry<V,Number> getNextVertex() {
+            V v = unknownVertices.remove();
+            Double dist = (Double) estimatedDistances.remove(v);
+            distances.put(v, dist);
+            return new BasicMapEntry<V,Number>(v, dist);
+        }
+
+        protected void update(V dest, E tentative_edge, double new_dist) {
+            estimatedDistances.put(dest, new_dist);
+            unknownVertices.update(dest);
+        }
+
+        protected void createRecord(V w, E e, double new_dist) {
+            estimatedDistances.put(w, new_dist);
+            unknownVertices.add(w);
+        }
+
+        protected void restoreVertex(V v, double dist) {
+            estimatedDistances.put(v, dist);
+            unknownVertices.add(v);
+            distances.remove(v);
+        }
+    }
+
+    public static interface Transformer<I,O> {
+
+        /**
+         * Transforms the input object (leaving it unchanged) into some output object.
+         * 
+         * @param input
+         *            the object to be transformed, should be left unchanged
+         * @return a transformed object
+         * @throws ClassCastException
+         *             (runtime) if the input is the wrong class
+         * @throws IllegalArgumentException
+         *             (runtime) if the input is invalid
+         * @throws FunctorException
+         *             (runtime) if the transform cannot be completed
+         */
+        public O transform(I input);
+
+    }
+
+    public static class ConstantTransformer<T> implements Transformer<Object,T> {
+
+        private T constant;
+
+        public ConstantTransformer(T constantToReturn) {
+            super();
+            constant = constantToReturn;
+        }
+
+        public T transform(Object input) {
+            return constant;
+        }
+
+    }
+}
diff --git a/enhancement-engines/freebase-disambiguation/src/main/java/org/apache/stanbol/enhancer/engine/disambiguation/freebase/graph/FreebaseDisambiguatorEngine.java b/enhancement-engines/freebase-disambiguation/src/main/java/org/apache/stanbol/enhancer/engine/disambiguation/freebase/graph/FreebaseDisambiguatorEngine.java
new file mode 100644
index 0000000..3d8eade
--- /dev/null
+++ b/enhancement-engines/freebase-disambiguation/src/main/java/org/apache/stanbol/enhancer/engine/disambiguation/freebase/graph/FreebaseDisambiguatorEngine.java
@@ -0,0 +1,383 @@
+package org.apache.stanbol.enhancer.engine.disambiguation.freebase.graph;
+
+import static org.apache.stanbol.enhancer.servicesapi.rdf.Properties.ENHANCER_CONFIDENCE;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.clerezza.rdf.core.LiteralFactory;
+import org.apache.clerezza.rdf.core.MGraph;
+import org.apache.clerezza.rdf.core.UriRef;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.stanbol.enhancer.engine.disambiguation.freebase.graph.constants.FreebaseDisambiguatorEngineConstants;
+import org.apache.stanbol.enhancer.engine.disambiguation.freebase.graph.helper.FreebaseDisambiguatorEngineHelper;
+import org.apache.stanbol.enhancer.engine.disambiguation.mlt.DisambiguationData;
+import org.apache.stanbol.enhancer.engine.disambiguation.mlt.SavedEntity;
+import org.apache.stanbol.enhancer.engine.disambiguation.mlt.Suggestion;
+import org.apache.stanbol.enhancer.servicesapi.ContentItem;
+import org.apache.stanbol.enhancer.servicesapi.EngineException;
+import org.apache.stanbol.enhancer.servicesapi.EnhancementEngine;
+import org.apache.stanbol.enhancer.servicesapi.InvalidContentException;
+import org.apache.stanbol.enhancer.servicesapi.ServiceProperties;
+import org.apache.stanbol.enhancer.servicesapi.helper.ContentItemHelper;
+import org.apache.stanbol.enhancer.servicesapi.helper.EnhancementEngineHelper;
+import org.apache.stanbol.enhancer.servicesapi.impl.AbstractEnhancementEngine;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+import com.tinkerpop.blueprints.Edge;
+import com.tinkerpop.blueprints.Graph;
+import com.tinkerpop.blueprints.Vertex;
+import com.tinkerpop.blueprints.impls.neo4j.Neo4jGraph;
+
+@Component(immediate = true, metatype = true)
+@Service
+@Properties(value = {@Property(name = EnhancementEngine.PROPERTY_NAME, value = "freebase-disambiguation"),
+                     @Property(name = FreebaseDisambiguatorEngine.GRAPH_LOCATION)})
+public class FreebaseDisambiguatorEngine extends AbstractEnhancementEngine<IOException,RuntimeException>
+        implements EnhancementEngine, ServiceProperties {
+    public static final String GRAPH_LOCATION = "graph.location";
+
+    private static Logger log = LoggerFactory.getLogger(FreebaseDisambiguatorEngine.class);
+
+    /**
+     * The default value for the execution of this Engine. Currently set to
+     * {@link ServiceProperties#ORDERING_POST_PROCESSING} - 600.
+     * <p>
+     * This should ensure that this engines runs as one of the first engines of the post-processing phase
+     */
+    public static final Integer defaultOrder = ServiceProperties.ORDERING_POST_PROCESSING - 600;
+
+    /**
+     * The plain text might be required for determining the extraction context
+     */
+
+    public static final String PLAIN_TEXT_MIMETYPE = "text/plain";
+    /**
+     * Contains the only supported mime type {@link #PLAIN_TEXT_MIMETYPE}
+     */
+    public static final Set<String> SUPPORTED_MIMETYPES = Collections.singleton(PLAIN_TEXT_MIMETYPE);
+
+    /**
+     * <p>
+     * Graph (Neo4jGraph) containing the whole graph
+     * </p>
+     */
+    private Graph graph;
+
+    /*
+     * The following parameters describe the ratio of the original fise:confidence values and the
+     * disambiguation scores contributing to the final disambiguated fise:confidence
+     * 
+     * TODO: make configurable
+     */
+    /**
+     * Default ratio for Disambiguation (2.0)
+     */
+    public static final double DEFAULT_DISAMBIGUATION_RATIO = 2.0;
+    /**
+     * Default ratio for the original fise:confidence of suggested entities
+     */
+    public static final double DEFAULT_CONFIDENCE_RATIO = 1.0;
+
+    /**
+     * The weight for disambiguation scores <code>:= disRatio/(disRatio+confRatio)</code>
+     */
+    private double disambiguationWeight = DEFAULT_DISAMBIGUATION_RATIO
+                                          / (DEFAULT_DISAMBIGUATION_RATIO + DEFAULT_CONFIDENCE_RATIO);
+    /**
+     * The weight for the original confidence scores <code>:= confRatio/(disRatio+confRatio)</code>
+     */
+    private double confidenceWeight = DEFAULT_CONFIDENCE_RATIO
+                                      / (DEFAULT_DISAMBIGUATION_RATIO + DEFAULT_CONFIDENCE_RATIO);
+
+    /**
+     * The {@link LiteralFactory} used to create typed RDF literals
+     */
+    private final LiteralFactory literalFactory = LiteralFactory.getInstance();
+
+    /**
+     * Returns the properties containing the {@link ServiceProperties#ENHANCEMENT_ENGINE_ORDERING}
+     */
+    public Map<String,Object> getServiceProperties() {
+        return Collections.unmodifiableMap(Collections.singletonMap(ENHANCEMENT_ENGINE_ORDERING,
+            (Object) defaultOrder));
+    }
+
+    /**
+     * /**
+     * <p>
+     * Checks whether the current {@code ContentItem} can be enhanced or not
+     * </p>
+     * 
+     * @param ci
+     *            The ContentItem to be enhanced
+     * 
+     * @return An integer indicating whether the ContentItem can be enhanced or not. There are a few constants
+     *         that can be returned: CANNOT_ENHANCE, ENHANCE_SYNCHRONOUS, ENHANCE_ASYNC
+     */
+    public int canEnhance(ContentItem ci) throws EngineException {
+        // check if content is present
+        try {
+            if ((ContentItemHelper.getText(ci.getBlob()) == null)
+                || (ContentItemHelper.getText(ci.getBlob()).trim().isEmpty())) {
+                return CANNOT_ENHANCE;
+            }
+        } catch (IOException e) {
+            log.error("Failed to get the text for " + "enhancement of content: " + ci.getUri(), e);
+            throw new InvalidContentException(this, ci, e);
+        }
+        // default enhancement is synchronous enhancement
+        return ENHANCE_SYNCHRONOUS;
+    }
+
+    /**
+     * <p>
+     * This function first evaluates all the possible ambiguations of each text annotation detected. The ids
+     * of the referenced entities are used to check relations in the Freebase graph in order to obtain a new
+     * disambiguation score based on the relations between entities in the graph
+     * </p>
+     * 
+     * <p>
+     * The results obtained are used to calculate new confidence values which are updated in the metadata.
+     * </p>
+     */
+    public void computeEnhancements(ContentItem ci) throws EngineException {
+
+        MGraph metadata = ci.getMetadata();
+
+        // Read the data from the content item
+        DisambiguationData disData;
+        ci.getLock().readLock().lock();
+        try {
+            disData = DisambiguationData.createFromContentItem(ci);
+        } finally {
+            ci.getLock().readLock().unlock();
+        }
+
+        /*
+         * Disambiguation Steps
+         * 
+         * 1. Generate the sets of entities for each text annotation
+         * 
+         * 2. Generate all the possible solutions for the text. That means, generate the combination of
+         * entities of each set (cartesian product)
+         * 
+         * 3. Construct the subgraph from the whole graph using only the extracted entities
+         * 
+         * 4. Filter the possible solutions, removing those entities from each possible solution which are not
+         * valid, i.e isolated entities in the graph
+         * 
+         * 5. For each possible solution, calculate the shortest path between every pair of entities in it and
+         * store the disambiguation score for the possible solution
+         * 
+         * 6. For each entity annotation, modify the confidence value using the disambiguation score for the
+         * suggestion of the entity annotation
+         */
+
+        /*
+         * (1) Generating the Map TextAnnotation->List<UriRef EntityUri> to be used to generate the possible
+         * solutions using cartesian product (one element for each entity of every text annotation)
+         * 
+         * Generating the allEntities list to be used to generate the subgraph
+         */
+        List<UriRef> allEntities = new ArrayList<UriRef>();
+        Multimap<UriRef,Suggestion> taSuggestions = ArrayListMultimap.create();
+
+        for (UriRef textAnnotation : disData.textAnnotations.keySet()) {
+            SavedEntity savedEntity = disData.textAnnotations.get(textAnnotation);
+            for (Suggestion suggestion : savedEntity.getSuggestions()) {
+                taSuggestions.put(textAnnotation, suggestion);
+                allEntities.add(suggestion.getEntityUri());
+            }
+        }
+
+        /*
+         * (2) Generate possible solutions
+         */
+        Set<List<Suggestion>> possibleSolutions = FreebaseDisambiguatorEngineHelper
+                .generatePossibleSolutions(taSuggestions);
+
+        /*
+         * (3) Generating the subgraph
+         */
+        log.info("Generating Subgraph for current entities");
+        Graph subgraph = FreebaseDisambiguatorEngineHelper.generateSubgraph(this.graph, allEntities);
+        log.info("Subgraph generated");
+
+        /*
+         * Stops the disambiguation if the subgraph has no edges
+         */
+        if (!subgraph.getEdges().iterator().hasNext()) {
+            // Graph with all its vertices isolated, don't modify the confidence
+            log.info("Graph with all its vertices isolated, don't modify the confidence of EntityAnnotation's");
+            return;
+        }
+
+        this.dumpGraph(subgraph);
+
+        /*
+         * (4) Filter possible solutions;
+         */
+        possibleSolutions = FreebaseDisambiguatorEngineHelper.filterPossibleSolutions(possibleSolutions,
+            subgraph);
+
+        /*
+         * (5) Calculate the shortest-path between entities in the possible solution and modify the
+         * disambiguation score of each Suggestion
+         */
+
+        FreebaseDisambiguatorEngineHelper.calculateDisambiguationScores(possibleSolutions, subgraph, disData);
+
+        /*
+         * (6) Modify confidence values of the entity annotations
+         */
+        ci.getLock().writeLock().lock();
+        try {
+            this.disambiguateAndApplyResults(metadata, disData);
+        } finally {
+            ci.getLock().writeLock().unlock();
+        }
+
+    }
+
+    /**
+     * <p>
+     * Calculates the new confidence value for each Suggestion using the normalized disambiguation score of
+     * the Suggestion
+     * <p>
+     * Applies the disambiguation results to the enhancements, modifying the confidence value of the entity
+     * annotations
+     * </p>
+     * 
+     * @param metadata
+     *            the {@code MGraph} instance containing the enhancement triples
+     * @param disData
+     *            the {@code DisambiguationData} instance containing the Suggestion objects and their
+     *            normalized disambiguation scores
+     */
+    private void disambiguateAndApplyResults(MGraph metadata, DisambiguationData disData) {
+        for (SavedEntity savedEntity : disData.textAnnotations.values()) {
+            for (Suggestion suggestion : savedEntity.getSuggestions()) {
+
+                Double ns = suggestion.getNormalizedDisambiguationScore();
+                /*
+                 * Suggestions with null in disambiguated confidence means that they don't form part of any
+                 * possible solution so their confidence value won't change
+                 */
+                if (ns == null) {
+                    suggestion.setDisambiguatedConfidence(suggestion.getOriginalConfidnece());
+                    continue;
+                }
+
+                Double c = suggestion.getOriginalConfidnece() == null ? 0 : suggestion
+                        .getOriginalConfidnece();
+
+                Double dc = c * confidenceWeight + ns * disambiguationWeight;
+                suggestion.setDisambiguatedConfidence(dc);
+
+                if (suggestion.getDisambiguatedConfidence() != null) {
+                    // change the confidence
+                    log.info("Modifying confidence for "
+                             + suggestion.getEntityAnnotation().getUnicodeString() + " (Entity "
+                             + suggestion.getEntityUri().getUnicodeString() + ") -> Last confidence: "
+                             + suggestion.getOriginalConfidnece() + " - New confidence: "
+                             + suggestion.getDisambiguatedConfidence());
+                    EnhancementEngineHelper.set(metadata, suggestion.getEntityAnnotation(),
+                        ENHANCER_CONFIDENCE, suggestion.getDisambiguatedConfidence(), literalFactory);
+                    EnhancementEngineHelper.addContributingEngine(metadata, suggestion.getEntityAnnotation(),
+                        this);
+                }
+            }
+        }
+
+    }
+
+    /**
+     * <p>
+     * Dump the graph to the log debug
+     * </p>
+     * 
+     * @param graph
+     *            the {@code Graph> to dump
+
+     */
+    private void dumpGraph(Graph graph) {
+        if (log.isDebugEnabled() || true) {
+            log.debug("Dumping graph");
+            for (Vertex vertex : graph.getVertices()) {
+                log.debug("Vertex "
+                          + vertex
+                          + " : "
+                          + vertex.getProperty(FreebaseDisambiguatorEngineConstants.VERTEX_ENTITY_URI_PROPERTY));
+            }
+            for (Edge edge : graph.getEdges()) {
+                log.debug("Edge: " + edge);
+            }
+        }
+    }
+
+    /**
+     * Activate and read the properties
+     * 
+     * @param ce
+     *            the {@link ComponentContext}
+     */
+    @Activate
+    protected void activate(ComponentContext ce) throws ConfigurationException {
+        try {
+            super.activate(ce);
+            @SuppressWarnings("unchecked")
+            Dictionary<String,Object> properties = ce.getProperties();
+            // update the service URL if it is defined
+            // if (properties.get(FORMCEPT_SERVICE_URL) != null) {
+            // this.serviceURL = (String) properties.get(FORMCEPT_SERVICE_URL);
+            // }
+            if (properties.get(GRAPH_LOCATION) != null
+                && !((String) properties.get(GRAPH_LOCATION)).isEmpty()) {
+                String location = (String) properties.get(GRAPH_LOCATION);
+                log.info("Loading graph. Location: " + location);
+                this.graph = new Neo4jGraph(location);
+                log.info("Graph loaded");
+            } else throw new IOException(GRAPH_LOCATION
+                                         + " property is null or empty. Failed to initialize the engine");
+        } catch (IOException e) { // log
+            log.error("Failed to update the configuration");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
+
+    /**
+     * Deactivate
+     * 
+     * @param ce
+     *            the {@link ComponentContext}
+     */
+    @Deactivate
+    protected void deactivate(ComponentContext ce) {
+        try {
+            // Closing the graph
+            super.deactivate(ce);
+            if (this.graph != null) this.graph.shutdown();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+}
diff --git a/enhancement-engines/freebase-disambiguation/src/main/java/org/apache/stanbol/enhancer/engine/disambiguation/freebase/graph/UndirectedGraphJung.java b/enhancement-engines/freebase-disambiguation/src/main/java/org/apache/stanbol/enhancer/engine/disambiguation/freebase/graph/UndirectedGraphJung.java
new file mode 100644
index 0000000..dcee663
--- /dev/null
+++ b/enhancement-engines/freebase-disambiguation/src/main/java/org/apache/stanbol/enhancer/engine/disambiguation/freebase/graph/UndirectedGraphJung.java
@@ -0,0 +1,91 @@
+package org.apache.stanbol.enhancer.engine.disambiguation.freebase.graph;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import com.tinkerpop.blueprints.Direction;
+import com.tinkerpop.blueprints.Edge;
+import com.tinkerpop.blueprints.Graph;
+import com.tinkerpop.blueprints.Vertex;
+import com.tinkerpop.blueprints.oupls.jung.GraphJung;
+
+/**
+ * <p>
+ * Custom implementation of GraphJung which allows the graph to be undirected, i.e, not taking into account
+ * the direction of the edges
+ * </p>
+ * 
+ * @author Antonio David Perez Morales <adperezmorales@gmail.com>
+ * 
+ */
+public class UndirectedGraphJung extends GraphJung<Graph> {
+
+    /**
+     * <p>
+     * Default constructor
+     * </p>
+     * 
+     * @param graph
+     *            The {@code Graph} object to be used as raw graph for JUNG implementation
+     */
+    public UndirectedGraphJung(Graph graph) {
+        super(graph);
+    }
+
+    /**
+     * <p>
+     * Get the out edges for a vertex
+     * </p>
+     * <p>
+     * Returns the incoming and outgoing edges for the vertex
+     * </p>
+     * 
+     * @param vertex
+     *            The vertex
+     * @return The {@code Collection<Edge>} of edges
+     */
+    public Collection<Edge> getOutEdges(final Vertex vertex) {
+        return getEdgesForVertex(vertex);
+    }
+
+    /**
+     * <p>
+     * Get the in edges for a vertex
+     * </p>
+     * <p>
+     * Returns the incoming and outgoing edges for the vertex
+     * </p>
+     * 
+     * @param vertex
+     *            The vertex
+     * @return The {@code Collection<Edge>} of edges
+     */
+    public Collection<Edge> getInEdges(Vertex vertex) {
+        return getEdgesForVertex(vertex);
+    }
+
+    /**
+     * <p>
+     * Gets all the edges for a vertex, both incoming and outgoing edges
+     * </p>
+     * 
+     * @param vertex
+     *            The vertex
+     * @return The {@code Collection<Edge>} of edges
+     * 
+     */
+    private Collection<Edge> getEdgesForVertex(Vertex vertex) {
+        final Iterable<Edge> itty = vertex.getEdges(Direction.BOTH);
+        if (itty instanceof Collection) {
+            return (Collection<Edge>) itty;
+        } else {
+            final List<Edge> edges = new ArrayList<Edge>();
+            for (final Edge edge : itty) {
+                edges.add(edge);
+            }
+            return edges;
+        }
+    }
+
+}
diff --git a/enhancement-engines/freebase-disambiguation/src/main/java/org/apache/stanbol/enhancer/engine/disambiguation/freebase/graph/constants/FreebaseDisambiguatorEngineConstants.java b/enhancement-engines/freebase-disambiguation/src/main/java/org/apache/stanbol/enhancer/engine/disambiguation/freebase/graph/constants/FreebaseDisambiguatorEngineConstants.java
new file mode 100644
index 0000000..c71342d
--- /dev/null
+++ b/enhancement-engines/freebase-disambiguation/src/main/java/org/apache/stanbol/enhancer/engine/disambiguation/freebase/graph/constants/FreebaseDisambiguatorEngineConstants.java
@@ -0,0 +1,36 @@
+package org.apache.stanbol.enhancer.engine.disambiguation.freebase.graph.constants;
+
+/**
+ * <p>
+ * Class containing the constants used by the Freebase Disambiguator Engine
+ * </p>
+ * 
+ * @author Antonio David Perez Morales <adperezmorales@gmail.com>
+ * 
+ */
+public class FreebaseDisambiguatorEngineConstants {
+    /**
+     * Property in a Vertex to put the URI
+     */
+    public static final String VERTEX_ENTITY_URI_PROPERTY = "URI";
+
+    /**
+     * Edge label representing direct connections
+     */
+    public static final String DIRECT_CONNECTION_EDGE_LABEL = "direct-connection";
+
+    /**
+     * Edge label representing mediated connections
+     */
+    public static final String MEDIATED_CONNECTION_EDGE_LABEL = "mediated-connection";
+
+    /**
+     * Edge property containing the property used for generate an index for the edges
+     */
+    public static final String EDGE_KEY_VERTICES_CONNECTED_PROPERTY = "vertices.connected";
+
+    /**
+     * Edge property used to store the calculated weight in the subgraph
+     */
+    public static final String WEIGHT_EDGE_PROPERTY = "weight";
+}
diff --git a/enhancement-engines/freebase-disambiguation/src/main/java/org/apache/stanbol/enhancer/engine/disambiguation/freebase/graph/helper/FreebaseDisambiguatorEngineHelper.java b/enhancement-engines/freebase-disambiguation/src/main/java/org/apache/stanbol/enhancer/engine/disambiguation/freebase/graph/helper/FreebaseDisambiguatorEngineHelper.java
new file mode 100644
index 0000000..1f9cb46
--- /dev/null
+++ b/enhancement-engines/freebase-disambiguation/src/main/java/org/apache/stanbol/enhancer/engine/disambiguation/freebase/graph/helper/FreebaseDisambiguatorEngineHelper.java
@@ -0,0 +1,453 @@
+package org.apache.stanbol.enhancer.engine.disambiguation.freebase.graph.helper;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.clerezza.rdf.core.UriRef;
+import org.apache.log4j.Logger;
+import org.apache.stanbol.enhancer.engine.disambiguation.freebase.graph.CustomDijkstraDistance;
+import org.apache.stanbol.enhancer.engine.disambiguation.freebase.graph.UndirectedGraphJung;
+import org.apache.stanbol.enhancer.engine.disambiguation.freebase.graph.constants.FreebaseDisambiguatorEngineConstants;
+import org.apache.stanbol.enhancer.engine.disambiguation.mlt.DisambiguationData;
+import org.apache.stanbol.enhancer.engine.disambiguation.mlt.SavedEntity;
+import org.apache.stanbol.enhancer.engine.disambiguation.mlt.Suggestion;
+
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Table;
+import com.tinkerpop.blueprints.Direction;
+import com.tinkerpop.blueprints.Edge;
+import com.tinkerpop.blueprints.Graph;
+import com.tinkerpop.blueprints.KeyIndexableGraph;
+import com.tinkerpop.blueprints.Vertex;
+import com.tinkerpop.blueprints.impls.tg.TinkerGraph;
+
+/**
+ * <p>
+ * Helper class with utility methods for FreebaseDisambiguatorEngine
+ * </p>
+ * 
+ * @author Antonio David Perez Morales <adperezmorales@gmail.com>
+ * 
+ */
+public class FreebaseDisambiguatorEngineHelper {
+    private static Logger log = Logger.getLogger(FreebaseDisambiguatorEngineHelper.class);
+
+    /**
+     * <p>
+     * Generates the possible solutions. It means, generating all the possible combinations between the
+     * suggested entities for each text annotation
+     * </p>
+     * <p>
+     * It uses the cartesian product between sets to obtain them
+     * </p>
+     * <p>
+     * For example:
+     * 
+     * <pre>
+     * - TextAnnotation A -> Suggestions : [1,2]
+     *  - TextAnnotation B -> Suggestions : [3,4]
+     *  - TextAnnotation C -> Suggestions : [5]
+     * 
+     * - Result: [1,3,5] , [1,4,5] , [2,3,5] , [2,4,5]
+     * 
+     * </p>
+     * 
+     * @param textAnnotationSuggestions
+     * @return
+     */
+    public static Set<List<Suggestion>> generatePossibleSolutions(Multimap<UriRef,Suggestion> textAnnotationSuggestions) {
+        List<Set<Suggestion>> allSets = new ArrayList<Set<Suggestion>>();
+        for (UriRef ref : textAnnotationSuggestions.keySet()) {
+            allSets.add(ImmutableSet.copyOf(textAnnotationSuggestions.get(ref)));
+        }
+
+        Set<List<Suggestion>> possibleSolutions = Sets.cartesianProduct(allSets);
+
+        return possibleSolutions;
+    }
+
+    /**
+     * <p>
+     * Generates a subgraph containing only the given entities and their relations
+     * </p>
+     * 
+     * @param graph
+     *            The {@code Graph} containing the whole graph
+     * @param entities
+     *            The {@code List<UriRef>} containing the entities to be used to generate the subgraph
+     * @return a {@code Graph} instance containing the subgraph
+     */
+    public static Graph generateSubgraph(Graph graph, List<UriRef> entities) {
+        KeyIndexableGraph subgraph = new TinkerGraph();
+        UriRef[] ents = entities.toArray(new UriRef[0]);
+
+        int size = ents.length;
+        Map<UriRef,Vertex> targetMap = new HashMap<UriRef,Vertex>();
+        Map<UriRef,Vertex> sourceMap = new HashMap<UriRef,Vertex>();
+        for (int i = 0; i < size; i++) {
+            Iterator<Vertex> itv = graph.getVertices(
+                FreebaseDisambiguatorEngineConstants.VERTEX_ENTITY_URI_PROPERTY, ents[i].getUnicodeString())
+                    .iterator();
+            if (itv.hasNext()) {
+                Vertex newVertex = subgraph.addVertex(null);
+                sourceMap.put(ents[i], itv.next());
+                targetMap.put(ents[i], newVertex);
+                newVertex.setProperty(FreebaseDisambiguatorEngineConstants.VERTEX_ENTITY_URI_PROPERTY,
+                    ents[i].getUnicodeString());
+            }
+        }
+
+        for (int i = 0; i < size - 1; i++) {
+            for (int j = i + 1; j < size; j++) {
+                Vertex source = sourceMap.get(ents[i]);
+                Vertex target = sourceMap.get(ents[j]);
+                if (source == null || target == null) continue;
+
+                Iterator<Edge> ite = graph
+                        .getEdges(
+                            FreebaseDisambiguatorEngineConstants.EDGE_KEY_VERTICES_CONNECTED_PROPERTY,
+                            (String) source
+                                    .getProperty(FreebaseDisambiguatorEngineConstants.VERTEX_ENTITY_URI_PROPERTY)
+                                    + "|"
+                                    + target.getProperty(FreebaseDisambiguatorEngineConstants.VERTEX_ENTITY_URI_PROPERTY))
+                        .iterator();
+                Edge originalEdge = null;
+                while (ite.hasNext()) {
+                    originalEdge = ite.next();
+                    if (originalEdge.getLabel().equals(
+                        FreebaseDisambiguatorEngineConstants.DIRECT_CONNECTION_EDGE_LABEL)) break;
+
+                }
+
+                if (originalEdge == null) {
+                    ite = graph
+                            .getEdges(
+                                FreebaseDisambiguatorEngineConstants.EDGE_KEY_VERTICES_CONNECTED_PROPERTY,
+                                (String) target
+                                        .getProperty(FreebaseDisambiguatorEngineConstants.VERTEX_ENTITY_URI_PROPERTY)
+                                        + "|"
+                                        + source.getProperty(FreebaseDisambiguatorEngineConstants.VERTEX_ENTITY_URI_PROPERTY))
+                            .iterator();
+
+                    while (ite.hasNext()) {
+                        originalEdge = ite.next();
+                        if (originalEdge.getLabel().equals(
+                            FreebaseDisambiguatorEngineConstants.DIRECT_CONNECTION_EDGE_LABEL)) break;
+                    }
+                }
+
+                if (originalEdge != null) {
+                    log.info("Creating edge between " + targetMap.get(ents[i]).getProperty("URI") + " and "
+                             + targetMap.get(ents[j]).getProperty("URI") + " from the original edge "
+                             + originalEdge);
+                    Edge newEdge = subgraph.addEdge(null, targetMap.get(ents[i]), targetMap.get(ents[j]),
+                        originalEdge.getLabel());
+                    Double weight = 0.0;
+                    for (String edgeProperty : originalEdge.getPropertyKeys()) {
+                        if (!edgeProperty.contains(".")
+                            && originalEdge.getProperty(edgeProperty) instanceof Integer) weight += (Integer) originalEdge
+                                .getProperty(edgeProperty);
+                    }
+
+                    if (originalEdge.getLabel().equals(
+                        FreebaseDisambiguatorEngineConstants.DIRECT_CONNECTION_EDGE_LABEL)) weight = weight * 10;
+                    else weight = weight / 10;
+
+                    newEdge.setProperty(FreebaseDisambiguatorEngineConstants.WEIGHT_EDGE_PROPERTY, weight);
+                }
+
+                /*
+                 * for (Edge e : iterable) {
+                 * 
+                 * repetitions++; if (e.getVertex(Direction.IN).equals(target) ||
+                 * e.getVertex(Direction.OUT).equals(target)) { Edge newEdge = subgraph.addEdge(null,
+                 * targetMap.get(ents[i]), targetMap.get(ents[j]), "connection"); int weight = 0; for (String
+                 * edgeProperty : e.getPropertyKeys()) { if (!edgeProperty.contains(".") &&
+                 * e.getProperty(edgeProperty) instanceof Integer) weight += (Integer)
+                 * e.getProperty(edgeProperty); }
+                 * 
+                 * newEdge.setProperty("weight", weight); break; } }
+                 */
+
+            }
+        }
+
+        return subgraph;
+    }
+
+    /**
+     * <p>
+     * Generates a subgraph containing only the given entities and their relations
+     * </p>
+     * 
+     * @param graph
+     *            The {@code Graph} containing the whole graph
+     * @param entities
+     *            The {@code List<UriRef>} containing the entities to be used to generate the subgraph
+     * @return a {@code Graph} instance containing the subgraph
+     */
+    public static Graph generateSubgraphBak(Graph graph, List<UriRef> entities) {
+
+        // Using in-memory graph with Key Index support
+
+        KeyIndexableGraph subgraph = new TinkerGraph();
+        UriRef[] ents = entities.toArray(new UriRef[0]);
+
+        subgraph.createKeyIndex(FreebaseDisambiguatorEngineConstants.VERTEX_ENTITY_URI_PROPERTY, Vertex.class);
+
+        int size = ents.length;
+        Map<UriRef,Vertex> targetMap = new HashMap<UriRef,Vertex>();
+        Map<UriRef,Vertex> sourceMap = new HashMap<UriRef,Vertex>();
+        for (int i = 0; i < size; i++) {
+            Iterator<Vertex> itv = graph.getVertices(
+                FreebaseDisambiguatorEngineConstants.VERTEX_ENTITY_URI_PROPERTY, ents[i].getUnicodeString())
+                    .iterator();
+            if (itv.hasNext()) {
+                Vertex newVertex = subgraph.addVertex(null);
+                sourceMap.put(ents[i], itv.next());
+                targetMap.put(ents[i], newVertex);
+                newVertex.setProperty(FreebaseDisambiguatorEngineConstants.VERTEX_ENTITY_URI_PROPERTY,
+                    ents[i].getUnicodeString());
+            }
+        }
+
+        for (int i = 0; i < size - 1; i++) {
+            Vertex source = sourceMap.get(ents[i]);
+            UriRef[] dest = new UriRef[size - i - 1];
+            System.arraycopy(ents, i + 1, dest, 0, size - i - 1);
+
+            Iterable<Edge> iterable = source.getEdges(Direction.BOTH,
+                FreebaseDisambiguatorEngineConstants.DIRECT_CONNECTION_EDGE_LABEL,
+                FreebaseDisambiguatorEngineConstants.MEDIATED_CONNECTION_EDGE_LABEL);
+            int processed = 0;
+            int max = dest.length;
+            for (Edge e : iterable) {
+                if (processed == max || dest.length == 0) break;
+
+                for (int j = 0; j < dest.length; j++) {
+                    Vertex target = sourceMap.get(dest[j]);
+                    if (e.getVertex(Direction.IN).equals(target) || e.getVertex(Direction.OUT).equals(target)) {
+                        Edge newEdge = subgraph.addEdge(null, targetMap.get(ents[i]), targetMap.get(dest[j]),
+                            e.getLabel());
+                        Double weight = 0.0;
+                        for (String edgeProperty : e.getPropertyKeys()) {
+                            if (!edgeProperty.contains(".") && e.getProperty(edgeProperty) instanceof Integer) weight += (Integer) e
+                                    .getProperty(edgeProperty);
+                        }
+
+                        /*
+                         * if(e.getLabel().equals("direct-connection")) weight = weight/10; else weight =
+                         * weight*10;
+                         */
+
+                        newEdge.setProperty(FreebaseDisambiguatorEngineConstants.WEIGHT_EDGE_PROPERTY, weight);
+                        processed++;
+
+                        if (j + 1 == dest.length) dest = new UriRef[0];
+                        else dest = Arrays.copyOfRange(dest, j + 1, dest.length);
+
+                        break;
+                    }
+                }
+            }
+
+        }
+
+        return subgraph;
+    }
+
+    /**
+     * <p>
+     * Filter the possible solutions removing those entities which are isolated in the possible solution
+     * </p>
+     * 
+     * @param possibleSolutions
+     *            The {@code Set<List<Suggestion>>} instance containing the possible solutions
+     * 
+     * @param graph
+     *            The {@code Graph} object containing the graph to be queried
+     * 
+     * @return The {@code Set<List<Suggestion>>} instance containing the filtered possible solutions
+     * 
+     */
+    public static Set<List<Suggestion>> filterPossibleSolutions(Set<List<Suggestion>> possibleSolutions,
+                                                                Graph graph) {
+
+        Map<Suggestion,Boolean> toFilter = new HashMap<Suggestion,Boolean>();
+        Set<List<Suggestion>> filtered = Sets.newHashSet();
+
+        for (List<Suggestion> possibleSolution : possibleSolutions) {
+            List<Suggestion> filteredPossibleSolution = Lists.newArrayList();
+
+            for (Suggestion suggestion : possibleSolution) {
+                if (toFilter.containsKey(suggestion)) {
+                    if (toFilter.get(suggestion).equals(true)) continue;
+                    else filteredPossibleSolution.add(suggestion);
+                } else {
+                    Iterator<Vertex> itv = graph.getVertices(
+                        FreebaseDisambiguatorEngineConstants.VERTEX_ENTITY_URI_PROPERTY,
+                        suggestion.getEntityUri().getUnicodeString()).iterator();
+                    if (itv.hasNext()) {
+                        Vertex v = itv.next();
+
+                        if (!v.getEdges(Direction.BOTH).iterator().hasNext()) {
+                            // Isolated Vertex. Filter the subGraph
+                            toFilter.put(suggestion, true);
+                            log.debug("Isolated Vertex "
+                                      + v.getProperty(FreebaseDisambiguatorEngineConstants.VERTEX_ENTITY_URI_PROPERTY)
+                                      + ". Filtering");
+                        } else {
+                            filteredPossibleSolution.add(suggestion);
+                            toFilter.put(suggestion, false);
+                        }
+                    } else {
+                        // No vertex exists. Filter the subGraph
+                        toFilter.put(suggestion, true);
+                        log.debug("No vertex exists with URI " + suggestion.getEntityUri().getUnicodeString());
+                    }
+
+                }
+            }
+
+            if (filteredPossibleSolution.size() > 1) filtered.add(filteredPossibleSolution);
+        }
+
+        return filtered;
+    }
+
+    /**
+     * <p>
+     * Calculates the disambiguation score for each possible solution using the Dijkstra Algorithm
+     * (shortest-path)
+     * </p>
+     * 
+     * @param possibleSolutions
+     *            The {@code Set<List<Suggestion>>} object containing the possible solutions to be used
+     * @param graph
+     *            the {@code Graph} object containing the graph to use
+     */
+    public static void calculateDisambiguationScores(Set<List<Suggestion>> possibleSolutions,
+                                                     Graph graph,
+                                                     DisambiguationData disData) {
+
+        UndirectedGraphJung undirectedGraph = new UndirectedGraphJung(graph);
+        CustomDijkstraDistance<Vertex,Edge> dijkstra = new CustomDijkstraDistance<Vertex,Edge>(
+                undirectedGraph, new CustomDijkstraDistance.Transformer<Edge,Double>() {
+                    public Double transform(Edge input) {
+                        return (1.0 / (Double) input
+                                .getProperty(FreebaseDisambiguatorEngineConstants.WEIGHT_EDGE_PROPERTY));
+                    }
+                });
+
+        // For normalization purposes
+        Double maxDisambiguationScore = 0.0;
+        Double minDisambiguationScore = Double.MAX_VALUE;
+
+        /*
+         * Storing the shortest path between each pair of vertices, both A -> B and B -> A, because we are
+         * using undirected graph
+         */
+        Table<Vertex,Vertex,Double> precalculatedDistances = HashBasedTable.create();
+
+        // Filter subgraphs;
+        for (List<Suggestion> posSol : possibleSolutions) {
+            log.info("Processing possible solution: " + posSol);
+
+            Suggestion[] sga = posSol.toArray(new Suggestion[0]);
+            int size = sga.length;
+            Vertex source, target = null;
+
+            Double notNormalizedDisambiguatonScore = 0.0;
+
+            for (int i = 0; i < size - 1; i++) {
+                for (int j = i + 1; j < size; j++) {
+                    Iterator<Vertex> itv = graph.getVertices(
+                        FreebaseDisambiguatorEngineConstants.VERTEX_ENTITY_URI_PROPERTY,
+                        sga[i].getEntityUri().getUnicodeString()).iterator();
+                    Iterator<Vertex> itv2 = graph.getVertices(
+                        FreebaseDisambiguatorEngineConstants.VERTEX_ENTITY_URI_PROPERTY,
+                        sga[j].getEntityUri().getUnicodeString()).iterator();
+
+                    if (itv.hasNext()) source = itv.next();
+                    else break;
+
+                    if (itv2.hasNext()) target = itv2.next();
+                    else break;
+
+                    Double precalculated = precalculatedDistances.get(source, target);
+                    log.info("Precalculated between " + source.getProperty("URI") + " and "
+                             + target.getProperty("URI") + " is " + precalculated);
+                    if (precalculated == null) {
+                        log.info("Calculating distance");
+                        Number n = dijkstra.getDistance(source, target);
+                        n = n == null ? 0 : n;
+                        Double val = n.doubleValue();
+                        log.info("Storing precalculated between " + source.getProperty("URI") + " and "
+                                 + target.getProperty("URI") + " ->  " + val);
+                        precalculatedDistances.put(source, target, val);
+                        precalculatedDistances.put(target, source, val);
+                    }
+
+                    notNormalizedDisambiguatonScore += precalculatedDistances.get(source, target);
+                }
+            }
+
+            // Calculate the inverse to be used later in normalization
+            notNormalizedDisambiguatonScore = 1 / Math.log1p(notNormalizedDisambiguatonScore + 1);
+
+            if (notNormalizedDisambiguatonScore >= maxDisambiguationScore) maxDisambiguationScore = notNormalizedDisambiguatonScore;
+            if (notNormalizedDisambiguatonScore <= minDisambiguationScore) minDisambiguationScore = notNormalizedDisambiguatonScore;
+
+            /* Storing temporarily the not-normalized confidence for the suggestions */
+            for (Suggestion sugg : posSol) {
+                Double temp = sugg.getNormalizedDisambiguationScore();
+                if (temp == null) temp = 0.0;
+
+                // Change score only if the new score is greater than the old one
+                sugg.setNormalizedDisambiguationScore(notNormalizedDisambiguatonScore > temp ? notNormalizedDisambiguatonScore
+                        : temp);
+            }
+
+        }
+
+        if (minDisambiguationScore == Double.MAX_VALUE) minDisambiguationScore = 0.0;
+
+        /* Normalize confidences in the range of [0,1] for all Suggestion */
+        /*
+         * Those suggestion which don't belong to any possible solution won't have disambiguation score so,
+         * their confidence value won't change
+         */
+        for (SavedEntity entity : disData.textAnnotations.values()) {
+            for (Suggestion sugg : entity.getSuggestions()) {
+                if (sugg.getNormalizedDisambiguationScore() == null) {
+                    sugg.setNormalizedDisambiguationScore(0.0);
+                    continue;
+                }
+                // If maxDisambiguationScore equals to minDisambiguationScore and equals to current
+                // disambiguation
+                // score, then set the max normalized value (1.0)
+                Double normalizedDisambiguationScore = sugg.getNormalizedDisambiguationScore().equals(
+                    maxDisambiguationScore)
+                                                       && maxDisambiguationScore
+                                                               .equals(minDisambiguationScore) ? 1.0
+                        : (sugg.getNormalizedDisambiguationScore() - minDisambiguationScore)
+                          / (maxDisambiguationScore - minDisambiguationScore);
+
+                log.info(sugg.getEntityUri().getUnicodeString() + " -> Normalized disambiguation score: "
+                         + normalizedDisambiguationScore);
+
+                sugg.setNormalizedDisambiguationScore(normalizedDisambiguationScore);
+
+            }
+        }
+    }
+}
diff --git a/enhancement-engines/freebase-disambiguation/src/main/resources/blueprints-core/pom.xml b/enhancement-engines/freebase-disambiguation/src/main/resources/blueprints-core/pom.xml
new file mode 100644
index 0000000..971da86
--- /dev/null
+++ b/enhancement-engines/freebase-disambiguation/src/main/resources/blueprints-core/pom.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.tinkerpop.blueprints</groupId>
+		<artifactId>blueprints</artifactId>
+		<version>2.5.0-SNAPSHOT</version>
+		<relativePath>../pom.xml</relativePath>
+	</parent>
+	<artifactId>blueprints-core</artifactId>
+	<url>http://blueprints.tinkerpop.com</url>
+	<name>Blueprints-Core</name>
+	<description>Core interfaces and utilities for Blueprints</description>
+	<dependencies>
+		<dependency>
+			<groupId>org.codehaus.jettison</groupId>
+			<artifactId>jettison</artifactId>
+			<version>1.3.3</version>
+		</dependency>
+		<dependency>
+			<groupId>com.fasterxml.jackson.datatype</groupId>
+			<artifactId>jackson-datatype-json-org</artifactId>
+			<version>2.1.2</version>
+		</dependency>
+		<dependency>
+			<groupId>colt</groupId>
+			<artifactId>colt</artifactId>
+			<version>1.2.0</version>
+		</dependency>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>${junit.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>commons-configuration</groupId>
+			<artifactId>commons-configuration</artifactId>
+			<version>1.6</version>
+		</dependency>
+	</dependencies>
+	<build>
+		<directory>${basedir}/target</directory>
+		<finalName>${project.artifactId}-${project.version}</finalName>
+		<resources>
+			<resource>
+				<directory>${basedir}/src/main/resources
+                </directory>
+			</resource>
+		</resources>
+		<testResources>
+			<testResource>
+				<directory>${basedir}/src/test/resources
+                </directory>
+			</testResource>
+		</testResources>
+		<plugins>
+			<plugin>
+				<artifactId>maven-assembly-plugin</artifactId>
+				<version>2.3</version>
+				<executions>
+					<execution>
+						<phase>package</phase>
+						<goals>
+							<goal>attached</goal>
+						</goals>
+					</execution>
+				</executions>
+				<configuration>
+					<attach>false</attach>
+					<descriptors>
+						<descriptor>src/assembly/distribution.xml</descriptor>
+					</descriptors>
+					<finalName>${project.artifactId}-${project.version}</finalName>
+					<outputDirectory>target</outputDirectory>
+					<workDirectory>target/assembly/work</workDirectory>
+					<tarLongFileMode>warn</tarLongFileMode>
+				</configuration>
+			</plugin>
+			<plugin>
+				<artifactId>maven-jar-plugin</artifactId>
+				<configuration>
+					<archive>
+						<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+					</archive>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.felix</groupId>
+				<artifactId>maven-bundle-plugin</artifactId>
+				<version>2.3.7</version>
+				<extensions>true</extensions>
+				<configuration>
+					<archive>
+						<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+					</archive>
+					<instructions>
+						<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
+						<Bundle-Name>${project.artifactId}</Bundle-Name>
+						<Bundle-Version>${project.version}</Bundle-Version>
+						<Import-Package>!cern.colt.*, !com.fasterxml.jackson*,
+							!org.apache.commons.configuration*,*</Import-Package>
+					</instructions>
+				</configuration>
+				<executions>
+					<execution>
+						<id>bundle-manifest</id>
+						<phase>process-classes</phase>
+						<goals>
+							<goal>manifest</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+</project>
diff --git a/enhancement-engines/freebase-disambiguation/src/main/resources/blueprints-neo4j-graph/pom.xml b/enhancement-engines/freebase-disambiguation/src/main/resources/blueprints-neo4j-graph/pom.xml
new file mode 100644
index 0000000..7a1c45c
--- /dev/null
+++ b/enhancement-engines/freebase-disambiguation/src/main/resources/blueprints-neo4j-graph/pom.xml
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>com.tinkerpop.blueprints</groupId>
+		<artifactId>blueprints</artifactId>
+		<version>2.5.0-SNAPSHOT</version>
+		<relativePath>../pom.xml</relativePath>
+	</parent>
+	<artifactId>blueprints-neo4j-graph</artifactId>
+	<name>Blueprints-Neo4jGraph</name>
+	<description>Blueprints property graph implementation for the Neo4j graph database</description>
+	<properties>
+		<bundle.plugin.version>2.3.7</bundle.plugin.version>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>com.tinkerpop.blueprints</groupId>
+			<artifactId>blueprints-core</artifactId>
+			<version>${blueprints.version}</version>
+		</dependency>
+		<!--<dependency> <groupId>org.neo4j</groupId> <artifactId>neo4j</artifactId> 
+			<version>1.9.2</version> <type>pom</type> </dependency> -->
+		<dependency>
+			<groupId>org.apache.felix</groupId>
+			<artifactId>org.osgi.core</artifactId>
+			<version>1.0.0</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.neo4j</groupId>
+			<artifactId>neo4j-ha</artifactId>
+			<version>1.9.2</version>
+			<scope>compile</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.neo4j</groupId>
+			<artifactId>neo4j-management</artifactId>
+			<version>1.9.2</version>
+			<scope>compile</scope>
+		</dependency>
+		<dependency>
+			<groupId>com.tinkerpop.blueprints</groupId>
+			<artifactId>blueprints-test</artifactId>
+			<version>${blueprints.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>javax.transaction</groupId>
+			<artifactId>jta</artifactId>
+			<version>1.1</version>
+			<scope>compile</scope>
+		</dependency>
+		<dependency>
+			<groupId>com.tinkerpop.blueprints</groupId>
+			<artifactId>blueprints-graph-jung</artifactId>
+			<version>2.3.0</version>
+		</dependency>
+		<dependency>
+			<groupId>com.tinkerpop.gremlin</groupId>
+			<artifactId>gremlin-java</artifactId>
+			<version>2.3.0</version>
+		</dependency>
+	</dependencies>
+	<build>
+		<directory>${basedir}/target</directory>
+		<finalName>${project.artifactId}-${project.version}</finalName>
+		<resources>
+			<resource>
+				<directory>${basedir}/src/main/resources
+                </directory>
+			</resource>
+		</resources>
+		<testResources>
+			<testResource>
+				<directory>${basedir}/src/test/resources
+                </directory>
+			</testResource>
+		</testResources>
+		<plugins>
+			<plugin>
+				<artifactId>maven-jar-plugin</artifactId>
+				<configuration>
+					<archive>
+						<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+					</archive>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.felix</groupId>
+				<artifactId>maven-bundle-plugin</artifactId>
+				<version>${bundle.plugin.version}</version>
+				<extensions>true</extensions>
+				<configuration>
+					<archive>
+						<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+					</archive>
+					<instructions>
+						<Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
+						<Bundle-Name>${project.artifactId}</Bundle-Name>
+						<Bundle-Version>${project.version}</Bundle-Version>
+						<Bundle-Classpath>.</Bundle-Classpath>
+						<Import-Package>*;resolution:="optional"</Import-Package>
+						<Export-Package>edu.uci.ics.*, com.tinkerpop.blueprints.*</Export-Package>
+						<Embed-Dependency>*;scope=compile|runtime;inline=true</Embed-Dependency>
+						<Embed-Directory>lib/</Embed-Directory>
+						<Embed-Transitive>true</Embed-Transitive>
+						<Include-Resource>{maven-dependencies}</Include-Resource>
+					</instructions>
+				</configuration>
+				<executions>
+					<execution>
+						<id>bundle-manifest</id>
+						<phase>process-classes</phase>
+						<goals>
+							<goal>manifest</goal>
+						</goals>
+					</execution>
+					<execution>
+						<id>bundle-bundle</id>
+						<phase>package</phase>
+						<goals>
+							<goal>bundle</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+	<profiles>
+		<profile>
+			<id>create-osgi-bundles-from-dependencies</id>
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>org.apache.felix</groupId>
+						<artifactId>maven-bundle-plugin</artifactId>
+						<version>${bundle.plugin.version}</version>
+						<extensions>true</extensions>
+						<executions>
+							<execution>
+								<id>wrap-my-dependency</id>
+								<goals>
+									<goal>wrap</goal>
+								</goals>
+								<configuration>
+									<wrapImportPackage>!com.sun.tools</wrapImportPackage>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+	</profiles>
+</project>