public boolean loadFile(String filepath) {
		// Note: loadFile does NOT clear first
		
		boolean loaded = false;
		try {
			JSONParser parser = new JSONParser();
			JSONObject jsonObj = (JSONObject) parser.parse(new FileReader(filepath));
			
			// Load ingredients
			JSONArray jsonArray = (JSONArray) jsonObj.get("ingredients");
			String name;
			for (Object i : jsonArray) {
				JSONObject jsonIngredient = (JSONObject) i;
				name = (String) jsonIngredient.get(IngredientNameKey);
				Ingredient newIngredient = (new Ingredient.Builder(name))
						.type((String) jsonIngredient.get(IngredientTypeKey))
						.glycemicIndex(getDouble(jsonIngredient, IngredientGlycemicKey))
						.fractionCarbs(getDouble(jsonIngredient, IngredientCarbsKey))
						.fractionFats(getDouble(jsonIngredient, IngredientFatsKey))
						.fractionProteins(getDouble(jsonIngredient, IngredientProteinsKey))
						.fractionFibers(getDouble(jsonIngredient, IngredientFibersKey))
						.calories(getDouble(jsonIngredient, IngredientCaloriesKey))
						.build();
				// Get properties and add..
				ingredients.put(name.toLowerCase(), newIngredient);
			}
			
			// Load courses
			jsonArray = (JSONArray) jsonObj.get("courses");
			//Course.Builder courseProperties;
			JSONArray interval;
			for (Object rawCourse : jsonArray) {
				JSONObject jsonCourse = (JSONObject) rawCourse;
				name = (String) jsonCourse.get("name");
				// Get all properties
				//courseProperties = new Course.Builder(name);
				
				ArrayList<RecipeConstraint> constraints = new ArrayList<RecipeConstraint>();				
				interval = (JSONArray) jsonCourse.get("gl");
				constraints.add(new MaximumGlycemicLoad((double) interval.get(1)));
				
				interval = (JSONArray) jsonCourse.get("fats");
				constraints.add(new MaximumSaturatedFat((double) interval.get(1))); 
				
				interval = (JSONArray) jsonCourse.get("proteins");
				constraints.add(new MinimumProtein((double) interval.get(0)));
				constraints.add(new MaximumProtein((double) interval.get(1)));
				
				interval = (JSONArray) jsonCourse.get("fibers");
				constraints.add(new MinimumFiber((double) interval.get(0)));
				
				interval = (JSONArray) jsonCourse.get("calories");
				constraints.add(new MinimumCalories((double) interval.get(0)));
				constraints.add(new MaximumCalories((double) interval.get(1)));
				
				Course course = new Course(name, constraints);				
				
				// Add..
				courses.put(name.toLowerCase(), course);
			}
			
			loaded = true;
			
			// Load substitutions
			jsonArray = (JSONArray) jsonObj.get("substitutions");
			Ingredient original, substitute;
			for (Object substitution : jsonArray) {
				JSONObject jsonSubstitution = (JSONObject) substitution;
				name = (String) jsonSubstitution.get("with");
				if ((substitute = getIngredient(name)) == null) {
					System.out.println(String.format("Fatal: The ingredient '%s' is not part of the Knowledge Base.", name));
					loaded = false;
				}
				name = (String) jsonSubstitution.get("replace");
				if ((original = getIngredient(name)) == null) {
					System.out.println(String.format("Fatal: The ingredient '%s' is not part of the Knowledge Base.", name));
					loaded = false;
				}
				specificSubstitutions.put(name.toLowerCase(), new Substitution(original, substitute, (double) jsonSubstitution.get("scale")));
			}
		} catch (FileNotFoundException ex) {
			System.out.println(String.format("File not found (%s)", ex.getMessage()));
			ex.printStackTrace();
		} catch (IOException ex) {
			System.out.println(String.format("Input error (%s)", ex.getMessage()));
			ex.printStackTrace();
		} catch (ParseException ex) {
			System.out.println(String.format("Parse error (%s)", ex.getMessage()));
			ex.printStackTrace();
		} catch (Exception ex) {
			System.out.println(String.format("Error (%s)", ex.getMessage()));
			ex.printStackTrace();
		}
		return loaded;
	}