/**
* The contents of this file are subject to the OpenMRS Public License
* Version 1.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://license.openmrs.org
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* Copyright (C) OpenMRS, LLC. All Rights Reserved.
*/
package org.openmrs.web.controller.query;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.openmrs.Location;
import org.openmrs.LocationTag;
import org.openmrs.api.context.Context;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* This class contains ajax-accessible queries relating to Locations
*/
@Controller
public class LocationQueryController {
@RequestMapping("/q/locationHierarchy")
public @ResponseBody
List<Map<String, Object>> getHierarchyAsJson(@RequestParam("selectLeafOnly") boolean selectLeafOnly,
@RequestParam(value = "selectableTags", required = false) List<String> selectableTags,
@RequestParam(value = "startFromTag", required = false) String startFromTag,
@RequestParam(value = "includeNullOption", required = false) Boolean includeNullOption) throws IOException {
HierarchyOptions options = new HierarchyOptions();
options.selectOnlyLeaves = selectLeafOnly;
options.selectableTags = selectableTags;
options.startFromTag = startFromTag;
options.includeNullOption = includeNullOption == null ? true : includeNullOption;
return getHierarchy(options); // returning a POJO will be handled by a spring Converter
}
/**
* Gets JSON formatted for jstree jquery plugin [ { data: ..., children: ...}, ... ]
*
* @return
* @throws IOException
*/
private List<Map<String, Object>> getHierarchy(HierarchyOptions options) throws IOException {
// TODO find a way to fetch all locations at once to avoid n+1 lazy-loads
List<Location> rootNodes = new ArrayList<Location>();
if (options.startFromTag != null) {
LocationTag tag = Context.getLocationService().getLocationTagByName(options.startFromTag);
rootNodes.addAll(Context.getLocationService().getLocationsByTag(tag));
} else {
for (Location loc : Context.getLocationService().getAllLocations()) {
if (loc.getParentLocation() == null) {
rootNodes.add(loc);
}
}
}
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
if (options.includeNullOption) {
list.add(toJsonHelper(null, options));
}
for (Location loc : rootNodes) {
list.add(toJsonHelper(loc, options));
}
return list;
}
/**
* { data: "Location's name (tags)", children: [ recursive calls to this method, ... ] }
*
* @param loc
* @return
*/
private Map<String, Object> toJsonHelper(Location loc, HierarchyOptions options) {
if (loc == null) {
String none = Context.getMessageSourceService().getMessage("general.none");
Map<String, Object> attrs = new HashMap<String, Object>();
attrs.put("id", 0);
attrs.put("name", none);
attrs.put("rel", "nulloption");
Map<String, Object> ret = new LinkedHashMap<String, Object>();
ret.put("attributes", attrs);
ret.put("data", none);
return ret;
} else {
String nodeType = isSelectable(loc, options) ? "selectable" : "default";
Map<String, Object> attrs = new HashMap<String, Object>();
attrs.put("id", loc.getLocationId());
attrs.put("name", loc.getName());
attrs.put("rel", nodeType);
Map<String, Object> ret = new LinkedHashMap<String, Object>();
ret.put("attributes", attrs);
StringBuilder sb = new StringBuilder(loc.getName());
if (loc.getTags() != null && loc.getTags().size() > 0) {
sb.append(" (");
for (Iterator<LocationTag> i = loc.getTags().iterator(); i.hasNext();) {
LocationTag t = i.next();
sb.append(t.getName());
if (i.hasNext())
sb.append(", ");
}
sb.append(")");
}
ret.put("data", sb.toString());
if (loc.getChildLocations() != null && loc.getChildLocations().size() > 0) {
List<Map<String, Object>> children = new ArrayList<Map<String, Object>>();
for (Location child : loc.getChildLocations())
children.add(toJsonHelper(child, options));
ret.put("children", children);
}
return ret;
}
}
/**
* Can this node be selected given the specified options?
*/
private boolean isSelectable(Location loc, HierarchyOptions options) {
if (options.selectOnlyLeaves && loc.getChildLocations() != null && loc.getChildLocations().size() > 0)
return false;
if (options.selectableTags != null && options.selectableTags.size() > 0) {
for (String tag : options.selectableTags)
if (loc.hasTag(tag))
return true;
return false;
}
return true;
}
class HierarchyOptions {
public boolean includeNullOption = true;
public boolean selectOnlyLeaves;
public List<String> selectableTags;
public String startFromTag;
}
}