public class AccountManager extends TextRazorConnection {

	public AccountManager(String apiKey) {
		super(apiKey);
	}
	
	public Account getAccount() throws NetworkException, AnalysisException {
		AccountResponse response = sendRequest("account/",
				null, 
				ContentType.JSON,
				"GET",
				AccountResponse.class);
		
		return response.getResponse();
	}

} public class Account {
	
	private String plan;
	
	private int concurrentRequestLimit;
	
	private int concurrentRequestsUsed;

	private int planDailyRequestsIncluded;

	private int requestsUsedToday;

	/**
	 * @return The ID of your current subscription plan.
	 */	
	public String getPlan() {
		return plan;
	}

	/**
	 * @return The maximum number of requests your account can make at the same time.
	 */	
	public int getConcurrentRequestLimit() {
		return concurrentRequestLimit;
	}

	/**
	 * @return The number of requests currently being processed by your account.
	 */
	public int getConcurrentRequestsUsed() {
		return concurrentRequestsUsed;
	}

	/**
	 * @return The daily number of requests included with your subscription plan.
	 */
	public int getPlanDailyRequestsIncluded() {
		return planDailyRequestsIncluded;
	}

	/**
	 * @return The total number of requests that have been made today.
	 */
	public int getRequestsUsedToday() {
		return requestsUsedToday;
	}
	
} public class Custom {

	public static class BoundVariable {

		public static class LinkedAnnotation {

			private String annotationName;

			private int linkedId;

			public String getAnnotationName() {
				return annotationName;
			}

			public int getLinkedId() {
				return linkedId;
			}
		}

		private String key;

		private List<Integer> intValue;

		private List<Float> floatValue;

		private List<String> stringValue;

		private List<byte[]> byteValue;

		private List<LinkedAnnotation> links;

		private List<Annotation> annotationValue = new ArrayList<Annotation>();
		
		private List<Word> wordValue = new ArrayList<Word>();
		private List<Sentence> sentenceValue = new ArrayList<Sentence>();
		private List<Relation> relationValue = new ArrayList<Relation>();
		private List<Entity> entityValue = new ArrayList<Entity>();
		private List<Property> propertyValue = new ArrayList<Property>();
		private List<NounPhrase> nounPhraseValue = new ArrayList<NounPhrase>();
		private List<Entailment> entailmentValue = new ArrayList<Entailment>();
		private List<Topic> topicValue = new ArrayList<Topic>();
		private List<Topic> coarseTopicValue = new ArrayList<Topic>();

		protected void addLinks(Response response) {
			if (null != links) {
				for (LinkedAnnotation link : links) {
					String annotationName = link.getAnnotationName();
					int linkedId = link.getLinkedId();

					if (annotationName.equals("word")) {
						Word word = response.getWords().get(linkedId);
						annotationValue.add(word);
						wordValue.add(word);
					}
					else if (annotationName.equals("sentence")) {
						Sentence sentence = response.getSentences().get(linkedId);
						annotationValue.add(sentence);
						sentenceValue.add(sentence);
					}
					else if (annotationName.equals("relation")) {
						Relation relation = response.getRelations().get(linkedId);
						annotationValue.add(relation);
						relationValue.add(relation);
					}
					else if (annotationName.equals("property")) {
						Property property = response.getProperties().get(linkedId);
						annotationValue.add(property);
						propertyValue.add(property);
					}
					else if (annotationName.equals("nounPhrase")) {
						NounPhrase nounPhrase = response.getNounPhrases().get(linkedId);
						annotationValue.add(nounPhrase);
						nounPhraseValue.add(nounPhrase);
					}
					else if (annotationName.equals("entailment")) {
						Entailment entailment = response.getEntailments().get(linkedId);
						annotationValue.add(entailment);
						entailmentValue.add(entailment);
					}
					else if (annotationName.equals("entity")) {
						Entity entity = response.getEntities().get(linkedId);
						annotationValue.add(entity);
						entityValue.add(entity);
					}
					else if (annotationName.equals("topic")) {
						Topic topic = response.getTopics().get(linkedId);
						annotationValue.add(topic);
						topicValue.add(topic);
					}
					else if (annotationName.equals("coarseTopic")) {
						Topic topic = response.getCoarseTopics().get(linkedId);
						annotationValue.add(topic);
						coarseTopicValue.add(topic);
					}
				}
			}
		}

		public String getKey() {
			return key;
		}

		public List<Integer> getIntValue() {
			return intValue;
		}

		public List<Float> getFloatValue() {
			return floatValue;
		}

		public List<String> getStringValue() {
			return stringValue;
		}

		public List<byte[]> getByteValue() {
			return byteValue;
		}

		public List<LinkedAnnotation> getLinks() {
			return links;
		}

		public List<Annotation> getAnnotationValue() {
			return annotationValue;
		}

		public List<Word> getWordValue() {
			return wordValue;
		}

		public List<Sentence> getSentenceValue() {
			return sentenceValue;
		}

		public List<Relation> getRelationValue() {
			return relationValue;
		}

		public List<Property> getPropertyValue() {
			return propertyValue;
		}

		public List<NounPhrase> getNounPhraseValue() {
			return nounPhraseValue;
		}

		public List<Entailment> getEntailmentValue() {
			return entailmentValue;
		}

		public List<Topic> getTopicValue() {
			return topicValue;
		}

		public List<Topic> getCoarseTopicValue() {
			return coarseTopicValue;
		}

		public List<Entity> getEntityValue() {
			return entityValue;
		}
	}

	private String name;

	private List<BoundVariable> contents;

	protected void addLinks(Response response) {
		if (null != contents) {
			for (BoundVariable variable : contents) {
				variable.addLinks(response);
			}
		}
	}

	public String getName() {
		return name;
	}

	public List<BoundVariable> getContents() {
		return contents;
	}

} public class Entity extends Annotation {
	
	private int id;
	
	private String sourceId;
	
	private String entityId;

	private String entityEnglishId;
	
	private String freebaseId;
	
	private String customEntityId;
	
	private String wikiLink;
	
	private String wikidataId;
	
	private String matchedText;
	
	private int startingPos;
	
	private int endingPos;
	
	private List<Integer> matchingTokens;
	
	private List<Word> matchingWords = new ArrayList<Word>();
	
	private List<String> freebaseTypes;
	
	private List<String> type;
	
	private double relevanceScore;
	
	private double confidenceScore;
	
	private String crunchbaseId;

	private String figi;

	private String permid;

	private String lei;
	
	private Map<String, List<String>> data = new HashMap<String, List<String>>();

	/**
	 * @return List of the {@link Word} objects that make up this entity.
	 */
	public List<Word> getMatchingWords() {
		return matchingWords;
	}

	/**
	 * @return The ID of this annotation.
	 */
	public int getId() {
		return id;
	}

	/**
	 * @return the ID for this entity, or null if this entity could not be disambiguated. This ID is from the localized Wikipedia for this document's language.
	 */
	public String getEntityId() {
		return entityId;
	}

	/**
	 * @return the disambiguated Freebase ID for this entity, or null if either this entity could not be disambiguated, or a Freebase link doesn't exist.
	 */
	public String getFreebaseId() {
		return freebaseId;
	}

	/**
	 * @return The custom Dictionary ID that this entity was matched from, or null if it was a TextRazor entity.
	 */
	public String getSourceId() {
		return sourceId;
	}
	
	/**
	 * @return The custom entity DictionaryEntry ID that matched this Entity, if this entity was matched in a custom dictionary.
	 */
	public String getCustomEntityId() {
		return customEntityId;
	}
	
	/**
	 * @return the full canonical link to Wikipedia for this entity, or null if either this entity could not be disambiguated or a Wikipedia link doesn't exist.
	 */
	public String getWikiLink() {
		return wikiLink;
	}
	
	/**
	 * @return the disambiguated Wikidata QID for this entity, or null if either this entity could not be disambiguated or a Wikidata link doesn't exist.
	 */
	public String getWikidataId() {
		return wikidataId;
	}

	/**
	 * @return the source text string that matched this entity.
	 */
	public String getMatchedText() {
		return matchedText;
	}

	/**
	 * @return The start offset in the input text for this entity. Note that TextRazor treats multi byte utf8 characters as a single position.
	 */
	public int getStartingPos() {
		return startingPos;
	}

	/**
	 * @return The end offset in the input text for this entity. Note that TextRazor treats multi byte utf8 characters as a single position.
	 */
	public int getEndingPos() {
		return endingPos;
	}


	/**
	 * @return List of Freebase types for this entity, or an empty list if there are none.
	 */
	public List<String> getFreebaseTypes() {
		return freebaseTypes;
	}

	/**
	 * @return List of DBPedia types for this entity, or an empty list if there are none.
	 */
	public List<String> getDBPediaTypes() {
		return type;
	}

	/**
	 * @return The relevance this entity has to the source text. This is a float on a scale of 0 to 1, with 1 being the most relevant. Relevance is determined by the contextual similarity between the entities context and facts in the TextRazor knowledgebase.
	 */
	public double getRelevanceScore() {
		return relevanceScore;
	}

	/**
	 * @return The confidence that TextRazor is correct that this is a valid entity. TextRazor uses an ever increasing number of signals to help spot valid entities, all of which contribute to this score. These include the contextual agreement between the words in the source text and our knowledgebase, agreement between other entities in the text, agreement between the expected entity type and context, prior probabilities of having seen this entity across wikipedia and other web datasets. The score ranges from 0.5 to 10, with 10 representing the highest confidence that this is a valid entity.
	 */
	public double getConfidenceScore() {
		return confidenceScore;
	}

	/**
	 * @return List of the word positions in the current sentence that make up this entity.
	 */
	public List<Integer> getMatchingTokens() {
		return matchingTokens;
	}

	/** 
	 * @return List of DBPedia types for this entity, or an empty array if there are none.
	 */
	public List<String> getType() {
		return type;
	}

	/**
	 * @return The disambiguated entityId in the English Wikipedia, where a link between localized and English ID could be found. Null if either the entity could not be linked, or where a language link did not exist.
	 */
	public String getEntityEnglishId() {
		return entityEnglishId;
	}
	
	/**
	 * @return The disambiguated Crunchbase ID for this entity. Null if either the entity could not be linked, or the entity was not a Company type.
	 */
	public String getCrunchbaseId() {
		return crunchbaseId;
	}

	/**
	 * @return The disambiguated Open FIGI for this entity. Null if either the entity could not be linked, or the entity was not a Company type.
	 */
	public String getFIGI() {
		return figi;
	}
	
	/**
	 * @return The disambiguated Thomson Reuters Open PermID for this entity. Null if either the entity could not be linked, or the entity was not a Company type.
	 */
	public String getPermId() {
		return permid;
	}
	
	/**
	 * @return The disambiguated Legal Entity Identifier for this entity. Null if either the entity could not be linked, or the entity was not a Company type.
	 */
	public String getLEI() {
		return lei;
	}
	
	/**
	 * @return A map of Query : List of result with data from enrichment queries that match this entity.
	 */
	public Map<String, List<String>> getData() {
		return data;
	}
	
	protected void addLinks(Response response) {
		if (null != matchingTokens) {
			for (Integer position : matchingTokens) {
				Word word = response.getWords().get(position);
				
				matchingWords.add(word);
				word.addEntity(this);
			}
		}
	}
		
} public class AnalyzedText extends TextRazorResponse {
	
	private Response response;
	
	/**
	 * @return The {@link Response} containing extracted TextRazor metadata.
	 */
	public Response getResponse() {
		return response;
	}
	
	public void createAnnotationLinks() {
		response.createAnnotationLinks();
	}
} public class ScoredCategory extends Annotation {
	
	private String categoryId;
	
	private String label;
	
	private String classifierId;
	
	private double score;

	/**
	 * @return The unique ID for this category within its classifier
	 */
	public String getCategoryId() {
		return categoryId;
	}

	/**
	 * @return The human readable label for this category.
	 */
	public String getLabel() {
		return label;
	}

	/**
	 * @return The unique identifier for the classifier that matched this category.
	 */
	public String getClassifierId() {
		return classifierId;
	}

	/**
	 * @return The score TextRazor has assigned to this category, between 0 and 1. A score closer to 1 indicates that this is a highly relevant category for our document.  
	 * 
	 * To avoid false positives you might want to ignore categories below a certain score - a good starting point would be 0.5.
	 * The best way to find an appropriate threshold is to run a sample set of your documents through the system and manually inspect the results.
	 */
	public double getScore() {
		return score;
	}
} public class Sentence extends Annotation {

	private List<Word> words = new ArrayList<Word>();
	
	private int position;
	
	/**
	 * @return The position of this sentence in your text.
	 */
	public int getPosition() {
		return position;
	}

	/**
	 * @return List of {@link Word} objects that are part of this sentence.
	 */
	public List<Word> getWords() {
		return words;
	}

} public class Topic extends Annotation {
	
	private int id;
	
	private String label;
	
	private String wikiLink;
	
	private String wikidataId;
	
	private double score;

	/**
	 * @return The ID of this annotation
	 */
	public int getId() {
		return id;
	}

	/**
	 * @return The textual label for this topic.
	 */
	public String getLabel() {
		return label;
	}

	/**
	 * @return Link to the Wikipedia page for this Topic, empty if there is no relevant link for this topic.
	 */
	public String getWikiLink() {
		return wikiLink;
	}
	
	/**
	 * @return The disambiguated Wikidata QID for this topic, or None if either this topic could not be disambiguated, or a Wikidata link doesn’t exist.
	 */
	public String getWikidataId() {
		return wikidataId;
	}

	/**
	 * @return The relevance of this topic to the processed document. This score ranges from 0 to 1, with 1 representing the highest relevance of the topic to the processed document.
	 */
	public double getScore() {
		return score;
	}

} public class Word extends Annotation {

	public static class Sense {
		
		private String synset;
		
		private float score;
		
		/**
		 * @return The WordNet 3.1 synset of this sense.
		 */
		public String getSynset() {
			return synset;
		}
		
		/**
		 * @return Score between 0 to 1 corresponding to the likelihood this sense applies to this word.
		 */
		public float getScore() {
			return score;
		}
	}
	
	public static class Suggestion {
		
		private String suggestion;
		
		private float score;
		
		/**
		 * @return The spelling suggestion.
		 */
		public String getSuggestion() {
			return suggestion;
		}
		
		/**
		 * @return Score between 0 to 1 representing the confidence that TextRazor has that this suggestion is correct.
		 */
		public float getScore() {
			return score;
		}
	}
	
	private int position;
	
	private int startingPos;
	
	private int endingPos;

	private String stem;
		
	private String lemma;
	
	private String token;
	
	private String partOfSpeech;
	
	private int parentPosition;
	
	private boolean hasParentPosition = false;
	
	private Word parentWord;
	
	private List<Word> children = new ArrayList<Word>();
	
	private String relationToParent;
	
	private List<Sense> senses = new ArrayList<Sense>();
	
	private List<Suggestion> spellingSuggestions = new ArrayList<Suggestion>();

	// Links to other annotations that this word is a part of.
	
	private List<Entailment> entailments = new ArrayList<Entailment>();
	
	private List<Entity> entities = new ArrayList<Entity>();
	
	private List<NounPhrase> nounPhrases = new ArrayList<NounPhrase>();
	
	private List<Property> propertyProperties = new ArrayList<Property>();
	
	private List<Property> propertyPredicates = new ArrayList<Property>();
	
	private List<Relation> relations = new ArrayList<Relation>();
	
	private List<RelationParam> relationParams = new ArrayList<RelationParam>();
	
	private List<Sentence> sentences = new ArrayList<Sentence>();
	
	/**
	 * @return The position of this word in its sentence.
	 */
	public int getPosition() {
		return position;
	}

	/**
	 * @return The start offset in the input text for this token. Note that TextRazor treats multi byte utf8 characters as a single position.
	 */
	public int getStartingPos() {
		return startingPos;
	}

	/**
	 * @return The end offset in the input text for this token. Note that TextRazor treats multi byte utf8 characters as a single position.
	 */
	public int getEndingPos() {
		return endingPos;
	}

	/**
	 * @return The stem of this word.
	 */
	public String getStem() {
		return stem;
	}

	/** 
	 * @return Returns the <a href="http://en.wikipedia.org/wiki/Lemma_(morphology)">morphological root</a> of this word
	 */
	public String getLemma() {
		return lemma;
	}

	/**
	 * @return The raw token string that matched this word in the source text.
	 */
	public String getToken() {
		return token;
	}

	/**
	 * @return The Part of Speech that applies to this word. We use the <a href="http://www.comp.leeds.ac.uk/ccalas/tagsets/upenn.html">Penn treebank</a> tagset.
	 */
	public String getPartOfSpeech() {
		return partOfSpeech;
	}

	/**
	 * @return True if this word has a grammatical parent.
	 */
	public boolean getHasParentPosition() {
		return hasParentPosition;
	}
	
	/**
	 * @return The position of the grammatical parent of this word, or null if this word is either at the root of the sentence or the "dependency-trees" extractor was not requested.
	 */
	public int getParentPosition() {
		return parentPosition;
	}
	
	public void setParentPosition(int parentPosition) {
		this.hasParentPosition = true;
		this.parentPosition = parentPosition;
	}

	/**
	 * @return The {@link Word} object of the grammatical parent of this word, or null if this word is either at the root of the sentence or the "dependency-trees" extractor was not requested.
	 */
	public Word getParentWord() {
		return parentWord;
	}

	/**
	 * @return List of the {@link Word} objects that are children of this word, empty list if the word has no children or the "dependency-trees" extractor was not requested.
	 */
	public List<Word> getChildren() {
		return children;
	}
	
	protected void addChild(Word child) {
		children.add(child);
	}
	
	/**
	 * @return List of {@link Sense} objects of this word.
	 */
	public List<Sense> getSenses() {
		return senses;
	}
	
	/**
	 * @return List of {@link Suggestion} objects of this word.
	 */
	public List<Suggestion> getSpellingSuggestions() {
		return spellingSuggestions;
	}
	
	/**
	 * @return The Grammatical relation between this word and it's parent, or null if this word is either at the root of the sentence or the "dependency-trees" extractor was not requested. TextRazor parses into <a href="http://nlp.stanford.edu/software/dependencies_manual.pdf">Stanford uncollapsed dependencies</a>.
	 */
	public String getRelationToParent() {
		return relationToParent;
	}

	/**
	 * @return List of the {@link Entailment} objects that this word is a part of.
	 */
	public List<Entailment> getEntailments() {
		return entailments;
	}

	protected void addEntailment(Entailment entailment) {
		entailments.add(entailment);
	}
	
	/**
	 * @return List of the {@link Entity} objects that this word is a part of.
	 */
	public List<Entity> getEntities() {
		return entities;
	}
	
	protected void addEntity(Entity entity) {
		entities.add(entity);
	}

	/**
	 * @return List of the {@link NounPhrase} objects that this word is a part of.
	 */
	public List<NounPhrase> getNounPhrases() {
		return nounPhrases;
	}
	
	protected void addNounPhrase(NounPhrase phrase) {
		nounPhrases.add(phrase);
	}

	/**
	 * @return List of the {@link Relation} objects that this word is a part of.
	 */
	public List<Relation> getRelations() {
		return relations;
	}
	
	protected void addRelation(Relation relation) {
		relations.add(relation);
	}

	/**
	 * @return List of the {@link Sentence} objects that this word is a part of.
	 */
	public List<Sentence> getSentences() {
		return sentences;
	}
	
	protected void addSentence(Sentence sentence) {
		sentences.add(sentence);
	}

	/**
	 * @return List of the {@link Property} objects that this word is a part of.
	 */
	public List<Property> getPropertyProperties() {
		return propertyProperties;
	}
	
	protected void addPropertyProperty(Property property) {
		propertyProperties.add(property);
	}

	/**
	 * @return List of the {@link Property} objects that this word is a part of.
	 */
	public List<Property> getPropertyPredicates() {
		return propertyPredicates;
	}

	protected void addPropertyPredicate(Property property) {
		propertyPredicates.add(property);
	}
	
	/**
	 * @return List of the {@link RelationParam} objects that this word is a part of.
	 */
	public List<RelationParam> getRelationParams() {
		return relationParams;
	}
	
	protected void addRelationParam(RelationParam param) {
		relationParams.add(param);
	}
	
	protected void addLinks(Response response) {
		if (hasParentPosition && 0 <= parentPosition) {
			parentWord = response.getWords().get(parentPosition);
			parentWord.addChild(this);
		}
	}
} public static class Sense {
		
	private String synset;
		
	private float score;
		
	/**
	 * @return The WordNet 3.1 synset of this sense.
	 */
	public String getSynset() {
		return synset;
	}
		
	/**
	 * @return Score between 0 to 1 corresponding to the likelihood this sense applies to this word.
	 */
	public float getScore() {
		return score;
	}
} public static class Suggestion {
		
	private String suggestion;
		
	private float score;
		
	/**
	 * @return The spelling suggestion.
	 */
	public String getSuggestion() {
		return suggestion;
	}
		
	/**
	 * @return Score between 0 to 1 representing the confidence that TextRazor has that this suggestion is correct.
	 */
	public float getScore() {
		return score;
	}
} public class ClassifierManager extends TextRazorConnection {
	public ClassifierManager(String apiKey) {
		super(apiKey);
	}

	public TextRazorResponse createClassifier(String classifierId, List<Category> categories) throws NetworkException, AnalysisException {
		String encodedId = encodeURLParam(classifierId);

		if (null == encodedId) {
			throw new RuntimeException("Invalid Classifier ID."); 
		}

		return sendRequest("categories/" + encodedId,
				categories, 
				ContentType.JSON,
				"PUT",
				TextRazorResponse.class);
	}
	
	public TextRazorResponse createClassifierWithCSV(String classifierId, String classifierFileName) throws IOException, NetworkException, AnalysisException {
		String encodedId = encodeURLParam(classifierId);

		if (null == encodedId) {
			throw new RuntimeException("Invalid Classifier ID."); 
		}
		
		String csvContents;
		
		BufferedReader br = new BufferedReader(new FileReader(classifierFileName));
		try {
		    StringBuilder sb = new StringBuilder();
		    String line = br.readLine();

		    while (line != null) {
		        sb.append(line);
		        sb.append('\n');
		        line = br.readLine();
		    }
		    
		    csvContents = sb.toString();
		} finally {
		    br.close();
		}
	
		return sendRequest("categories/" + encodedId,
				csvContents, 
				ContentType.CSV,
				"PUT",
				TextRazorResponse.class);
	}

	public TextRazorResponse deleteClassifier(String classifierId) throws NetworkException, AnalysisException {
		String encodedId = encodeURLParam(classifierId);

		if (null == encodedId) {
			throw new RuntimeException("Invalid Classifier ID."); 
		}

		return sendRequest("categories/" + encodedId,
				null, 
				ContentType.JSON,
				"DELETE",
				TextRazorResponse.class);
	}

	public TextRazorResponse deleteCategory(String classifierId, String categoryId) throws NetworkException, AnalysisException {
		String encodedClassifierId = encodeURLParam(classifierId);
		if (null == encodedClassifierId) {
			throw new RuntimeException("Invalid Classifier ID."); 
		}

		String encodedCategoryId = encodeURLParam(categoryId);
		if (null == encodedCategoryId) {
			throw new RuntimeException("Invalid Category ID."); 
		}
		
		return sendRequest("categories/" + encodedClassifierId + "/" + encodedCategoryId,
				null, 
				ContentType.JSON,
				"DELETE",
				TextRazorResponse.class);
	}

	public Category getCategory(String classifierId, String categoryId) throws NetworkException, AnalysisException {	
		String encodedClassifierId = encodeURLParam(classifierId);
		if (null == encodedClassifierId) {
			throw new RuntimeException("Invalid Classifier ID."); 
		}

		String encodedCategoryId = encodeURLParam(categoryId);
		if (null == encodedCategoryId) {
			throw new RuntimeException("Invalid Category ID."); 
		}
		
		GetCategoryResponse response = sendRequest("categories/" + encodedClassifierId + "/" + encodedCategoryId,
				null, 
				ContentType.JSON,
				"GET",
				GetCategoryResponse.class);
		
		return response.getResponse();
	}
	
	public PagedAllCategories allCategories(String classifierId, int limit, int offset) throws NetworkException, AnalysisException {	
		String encodedId = encodeURLParam(classifierId);

		if (null == encodedId) {
			throw new RuntimeException("Invalid Category ID."); 
		}

		QueryBuilder paramBuilder = new QueryBuilder();

		try {
			if (limit > 0) {
				paramBuilder.addParam("limit", Integer.toString(limit));
			}

			if (offset > 0) {
				paramBuilder.addParam("offset", Integer.toString(offset));
			}
		}
		catch (UnsupportedEncodingException e) {
			throw new RuntimeException("Unable to encode category paging paramaters."); 
		}

		AllCategoriesResponse response = sendRequest("categories/" + encodedId + "/_all?" + paramBuilder.build(),
				null, 
				ContentType.JSON,
				"GET",
				AllCategoriesResponse.class);
		
		return response.getResponse();
	}

	public PagedAllCategories allCategories(String classifierId, int limit) throws NetworkException, AnalysisException {	
		return allCategories(classifierId, limit, -1);
	}

	public PagedAllCategories allCategories(String classifierId) throws NetworkException, AnalysisException {	
		return allCategories(classifierId, -1);
	}
} public class Category {
	
	public static class Builder {
		private String categoryId;
		
		private String label;
		
		private String query;

		public Builder setCategoryId(String categoryId) {
			this.categoryId = categoryId;
			return this;
		}

		public Builder setLabel(String label) {
			this.label = label;
			return this;
		}

		public Builder setQuery(String query) {
			this.query = query;
			return this;
		}
		
		public Category build() {
			return new Category(categoryId, label, query);
		}	
	}
	
	private String categoryId;
	
	private String label;
	
	private String query;

	public Category() {}
	
	private Category(String categoryId, String label, String query) {
		this.categoryId = categoryId;
		this.label = label;
		this.query = query;
	}
	
	public static Builder builder() {
        return new Builder();
    }
	
	/**
	 * @return The unique ID for this category within its classifier
	 */
	public String getCategoryId() {
		return categoryId;
	}

	/**
	 * @return The human readable label for this category.
	 */
	public String getLabel() {
		return label;
	}

	/**
	 * @return The query used to define this category.
	 */
	public String getQuery() {
		return query;
	}
} public class DictionaryManager extends TextRazorConnection {

	public DictionaryManager(String apiKey) {
		super(apiKey);
	}

	public TextRazorResponse createDictionary(Dictionary dictionary) throws NetworkException, AnalysisException {
		String encodedId = encodeURLParam(dictionary.getId());

		if (null == encodedId) {
			throw new RuntimeException("Invalid Dictionary ID."); 
		}

		return sendRequest("entities/" + encodedId,
				dictionary, 
				ContentType.JSON,
				"PUT",
				TextRazorResponse.class);
	}

	public TextRazorResponse deleteDictionary(String id) throws NetworkException, AnalysisException {
		String encodedId = encodeURLParam(id);

		if (null == encodedId) {
			throw new RuntimeException("Invalid Dictionary ID."); 
		}

		return sendRequest("entities/" + encodedId,
				null, 
				ContentType.JSON,
				"DELETE",
				TextRazorResponse.class);
	}

	public TextRazorResponse deleteDictionary(Dictionary dictionary) throws NetworkException, AnalysisException {
		return deleteDictionary(dictionary.getId());
	}

	public List<Dictionary> allDictionaries() throws NetworkException, AnalysisException {	
		return sendRequest("entities/",
				null, 
				ContentType.JSON,
				"GET",
				AllDictionariesResponse.class).getDictionaries();
	}

	public Dictionary getDictionary(String id) throws NetworkException, AnalysisException {	
		String encodedId = encodeURLParam(id);

		if (null == encodedId) {
			throw new RuntimeException("Invalid Dictionary ID."); 
		}

		GetDictionaryResponse response = sendRequest("entities/" + encodedId,
				null, 
				ContentType.JSON,
				"GET",
				GetDictionaryResponse.class);
		
		return response.getResponse();
	}

	public PagedAllEntries allEntries(String id, int limit, int offset) throws NetworkException, AnalysisException {	
		String encodedId = encodeURLParam(id);

		if (null == encodedId) {
			throw new RuntimeException("Invalid Dictionary ID."); 
		}

		QueryBuilder paramBuilder = new QueryBuilder();

		try {
			if (limit > 0) {
				paramBuilder.addParam("limit", Integer.toString(limit));
			}

			if (offset > 0) {
				paramBuilder.addParam("offset", Integer.toString(offset));
			}
		}
		catch (UnsupportedEncodingException e) {
			throw new RuntimeException("Unable to encode dictionary paging paramaters."); 
		}

		AllEntriesResponse response = sendRequest("entities/" + encodedId + "/_all?" + paramBuilder.build(),
				null, 
				ContentType.JSON,
				"GET",
				AllEntriesResponse.class);
		
		return response.getResponse();
	}

	public PagedAllEntries allEntries(String id, int limit) throws NetworkException, AnalysisException {	
		return allEntries(id, limit, -1);
	}

	public PagedAllEntries allEntries(String id) throws NetworkException, AnalysisException {	
		return allEntries(id, -1);
	}

	public TextRazorResponse addEntries(String id, List<DictionaryEntry> entries) throws NetworkException, AnalysisException {	
		String encodedId = encodeURLParam(id);

		if (null == encodedId) {
			throw new RuntimeException("Invalid Dictionary ID."); 
		}

		return sendRequest("entities/" + encodedId + "/",
				entries, 
				ContentType.JSON,
				"POST",
				TextRazorResponse.class);
	}

	public DictionaryEntry getEntry(String dictionaryId, String entryId) throws NetworkException, AnalysisException {	
		String encodedDictionaryId = encodeURLParam(dictionaryId);
		String encodedEntryId = encodeURLParam(entryId);

		if (null == encodedDictionaryId) {
			throw new RuntimeException("Invalid Dictionary ID."); 
		}

		if (null == encodedEntryId) {
			throw new RuntimeException("Invalid Entry ID."); 
		}

		GetDictionaryEntryResponse response = sendRequest("entities/" + encodedDictionaryId + "/" + encodedEntryId,
				null, 
				ContentType.JSON,
				"GET",
				GetDictionaryEntryResponse.class);
		
		return response.getResponse();
	}

	public TextRazorResponse deleteEntry(String dictionaryId, String entryId) throws NetworkException, AnalysisException {	
		String encodedDictionaryId = encodeURLParam(dictionaryId);
		String encodedEntryId = encodeURLParam(entryId);

		if (null == encodedDictionaryId) {
			throw new RuntimeException("Invalid Dictionary ID."); 
		}

		if (null == encodedEntryId) {
			throw new RuntimeException("Invalid Entry ID."); 
		}

		return sendRequest("entities/" + encodedDictionaryId + "/" + encodedEntryId,
				null, 
				ContentType.JSON,
				"DELETE",
				TextRazorResponse.class);
	}

} public class Dictionary {
	public static class Builder {
		private String id;
		private String matchType = "token";
		private Boolean caseInsensitive = false;
		private String language = "any";
		
		public Builder setId(String id) {
			this.id = id;
			return this;
		}
		
		public Builder setMatchType(String matchType) {
			this.matchType = matchType;
			return this;
		}
		
		public Builder setCaseInsensitive(Boolean caseInsensitive) {
			this.caseInsensitive = caseInsensitive;
			return this;
		}
		
		public Builder setLanguage(String language) {
			this.language = language;
			return this;
		}
		
		public Dictionary build() {
			return new Dictionary(id, matchType, caseInsensitive, language);
		}	
	}
	
	private String id;
	private String matchType;
	private Boolean caseInsensitive;
	private String language;
	
	public Dictionary() {}
	
	public Dictionary(String id, String matchType, Boolean caseInsensitive, String language) {
		this.id = id;
		this.matchType = matchType;
		this.caseInsensitive = caseInsensitive;
		this.language = language;
	}

	public static Builder builder() {
        return new Builder();
    }
	
	public String getId() {
		return id;
	}
	
	public String getMatchType() {
		return matchType;
	}
	
	public Boolean getCaseInsensitive() {
		return caseInsensitive;
	}
	
	public String getLanguage() {
		return language;
	}
	
	
} public class DictionaryEntry {

	public static class Builder {
		private String id;
		
		private String text;
		
		private Map<String, List<String>> data = new HashMap<String, List<String>>();

		public Builder setId(String id) {
			this.id = id;
			return this;
		}

		public Builder setText(String text) {
			this.text = text;
			return this;
		}

		public Builder addData(String key, List<String> value) {
			this.data.put(key, value);
			return this;
		}
		
		public DictionaryEntry build() {
			return new DictionaryEntry(id, text, data);
		}	
	}
	
	private String id;
	
	private String text;
	
	private Map<String, List<String>> data = new HashMap<String, List<String>>();

	public DictionaryEntry() {}
	
	private DictionaryEntry(String id, String text, Map<String, List<String>> data) {
		this.id = id;
		this.text = text;
		this.data = data;
	}
	
	public static Builder builder() {
        return new Builder();
    }
	
	public String getId() {
		return id;
	}

	public String getText() {
		return text;
	}

	public Map<String, List<String>> getData() {
		return data;
	}
} public class PagedAllEntries {
	
	int total;
	
	int limit;
	
	int offset;
	
	List<DictionaryEntry> entries;

	public int getTotal() {
		return total;
	}

	public int getLimit() {
		return limit;
	}

	public int getOffset() {
		return offset;
	}

	public List<DictionaryEntry> getEntries() {
		return entries;
	}
	
}
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import com.textrazor.account.AccountManager;
import com.textrazor.account.model.Account;
import com.textrazor.annotations.Custom;
import com.textrazor.annotations.Entity;
import com.textrazor.annotations.AnalyzedText;
import com.textrazor.annotations.ScoredCategory;
import com.textrazor.annotations.Sentence;
import com.textrazor.annotations.Topic;
import com.textrazor.annotations.Word;
import com.textrazor.annotations.Word.Sense;
import com.textrazor.annotations.Word.Suggestion;
import com.textrazor.classifier.ClassifierManager;
import com.textrazor.classifier.model.Category;
import com.textrazor.dictionary.DictionaryManager;
import com.textrazor.dictionary.model.Dictionary;
import com.textrazor.dictionary.model.DictionaryEntry;
import com.textrazor.dictionary.model.PagedAllEntries;
package com.textrazor;



public class TestTextRazor {

	public static void testAccount(String apiKey) throws NetworkException, AnalysisException {
		AccountManager manager = new AccountManager(apiKey);
		
		Account account = manager.getAccount();
		
		System.out.println("Your current account plan is " + account.getPlan() + ", which includes " + account.getPlanDailyRequestsIncluded() + " daily requests, " + account.getRequestsUsedToday() + " used today");
	}
	
	public static void testClassifier(String apiKey) throws NetworkException, AnalysisException {
		ClassifierManager manager = new ClassifierManager(apiKey);
		
		String testClassifierId = "java_test_classifier";
		
		try {
			for (Category cat : manager.allCategories(testClassifierId).getCategories()) {
				System.out.println("Current category: " + cat.getCategoryId());
			}
		
			manager.deleteClassifier(testClassifierId);
		}
		catch (AnalysisException ex) {}
		
		System.out.println("Creating classifier..");
		
		try {
			manager.createClassifier(testClassifierId, 
					Arrays.asList(Category.builder()
							.setCategoryId("ID1")
							.setQuery("or(concept('sport>soccer'),concept('sport>association football'))")
							.build(),
							Category.builder()
							.setCategoryId("ID2")
							.setQuery("or(concept('sport>super bowl'),concept('sport>NFL'),concept('sport>american football'))")
							.build()));
			
		} catch (IOException e) {
			e.printStackTrace();
		}

		System.out.println("Finished Creating classifier");
		
		System.out.println(manager.getCategory(testClassifierId, "ID1").getCategoryId());
		
		{
			for (Category cat : manager.allCategories(testClassifierId).getCategories()) {
				System.out.println("Final Classifier: " + cat.getCategoryId() + " " + cat.getQuery());
			}
		}
	
		TextRazor client = new TextRazor(apiKey);
		
		String url = "http://us.cnn.com/2016/02/01/us/carolina-panthers-haters/index.html";
		
		client.setCleanupMode("cleanHTML");
		client.setClassifiers(Arrays.asList(testClassifierId));
		
		AnalyzedText response = client.analyzeUrl(url);
		
		for (ScoredCategory category : response.getResponse().getCategories()) {
			System.out.println("Category:" + category.getCategoryId() + " " + category.getLabel() + " " + category.getScore());
		}
	}
	
	
	public static void testDictionary(String apiKey) throws NetworkException, AnalysisException {
		DictionaryManager dictionaryManager = new DictionaryManager(apiKey);
	
		// Delete all dictionaries in our account.
		{
			for (Dictionary dict : dictionaryManager.allDictionaries()) {
				System.out.println("Deleting current dictionary: " + dict.getId());
				dictionaryManager.deleteDictionary(dict);
			}
		}

		// Create a new dictionary
		
		Dictionary newDict = Dictionary.builder().setId("developers").build();

		dictionaryManager.createDictionary(newDict);
		
		Dictionary teamsDictionary = dictionaryManager.getDictionary("developers");
		 
		System.out.println("Got dict:");
		 System.out.println(teamsDictionary.getId());

		 {
			List<DictionaryEntry> newEntries = new ArrayList<DictionaryEntry>();