package org.fuzzydb.samples.mvc; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import javax.validation.Valid; import org.fuzzydb.attrs.AttributeDefinitionService; import org.fuzzydb.core.query.Result; import org.fuzzydb.samples.GenericEntity; import org.fuzzydb.spring.repository.AttributeMatchQuery; import org.fuzzydb.spring.repository.FuzzyRepository; import org.fuzzydb.spring.repository.SubjectMatchQuery; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.transaction.annotation.Transactional; import org.springframework.ui.Model; import org.springframework.util.StringUtils; import org.springframework.validation.Errors; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import com.thoughtworks.xstream.XStream; public abstract class AbstractDataController<ENTITY> { @Autowired protected DataGenerator dataGenerator; @Autowired private AttributeDefinitionService attrDefs; public AbstractDataController() { super(); } /** * Insert a random item into the database */ abstract protected void createItem(); abstract protected String getDefaultSearchStyle(); abstract protected FuzzyRepository<ENTITY, String> getRepo(); abstract protected ENTITY getSearchForm(); abstract protected String getViewPathPrefix(); @Transactional @RequestMapping(value="/createItems", method=RequestMethod.GET) public String createItems(@RequestParam(defaultValue="5") int numItems) { for (int i = 0; i < numItems; i++) { createItem(); } return "redirect:search"; } @Transactional(readOnly=true) @RequestMapping(value="/search", method=RequestMethod.POST) public void search( @RequestParam(required=false) String style, @RequestParam(defaultValue="0") int start, @RequestParam(defaultValue="10") int pageSize, Model model, @ModelAttribute("command") @Valid ENTITY form, Errors result) { style = style == null ? getDefaultSearchStyle() : style; Pageable pageable = new PageRequest(start, pageSize); doSearch(getRepo(), model, style, null, pageable, form); } /** * * @param style The name of the matching configuration or 'style' * @param ref A database reference - something we probably don't want in a real app * @param maxResults This lets the database know the maximum number of results you're going to iterate over * you could potentially feed results out as you get them, so long as you've got the * transaction held open... hmmm. I feel some Ajax coming on ;) */ @Transactional(readOnly=true) @RequestMapping(value="/search", method=RequestMethod.GET) public void search( Model model, @RequestParam(required=false) String style, @RequestParam(required=false) String ref, @RequestParam(defaultValue="0") int start, @RequestParam(defaultValue="10") int pageSize) { style = style == null ? getDefaultSearchStyle() : style; // We need some attributes to search against. This doesn't have to be something already in the // database. For the default, we'll just grab a named sample from our dataGenerator. // If we have a key (ref), then we'll use that to grab an item. ENTITY idealMatch = StringUtils.hasText(ref) ? getRepo().findOne(ref) : getRepo().findFirst(); Pageable pageable = new PageRequest(start/pageSize, pageSize); doSearch(getRepo(), model, style, ref, pageable, idealMatch); model.addAttribute("command", getSearchForm()); } @Transactional(readOnly=true) @RequestMapping(value="/json/search", method=RequestMethod.GET, produces="application/json") @ResponseBody public Object jsonSearch( Model model, @RequestParam(required=false) String style, @RequestParam(required=false) String ref, @RequestParam(defaultValue="0") int start, @RequestParam(defaultValue="10") int pageSize) { style = style == null ? getDefaultSearchStyle() : style; // We need some attributes to search against. This doesn't have to be something already in the // database. For the default, we'll just grab a named sample from our dataGenerator. // If we have a key (ref), then we'll use that to grab an item. ENTITY idealMatch = StringUtils.hasText(ref) ? getRepo().findOne(ref) : getRepo().findFirst(); Pageable pageable = new PageRequest(start/pageSize, pageSize); doSearch(getRepo(), model, style, ref, pageable, idealMatch); return model; //.asMap().get("results"); } @Transactional(readOnly=true) @RequestMapping(value="/json/search", method=RequestMethod.POST, consumes="application/json", produces="application/json") @ResponseBody public Object jsonSearch( @RequestParam(required=false) String style, @RequestParam(defaultValue="0") int start, @RequestParam(defaultValue="10") int pageSize, Model model, @RequestBody @Valid GenericEntity form ) { //, // Errors result) { style = style == null ? getDefaultSearchStyle() : style; Pageable pageable = new PageRequest(start, pageSize); doSearch(getRepo(), model, style, null, pageable, (ENTITY) form); return model.asMap().get("results"); } @Transactional(readOnly=true) @RequestMapping(value="/json/dump", method=RequestMethod.GET, produces="application/json") @ResponseBody public Object jsonDump() throws IOException { Iterable<ENTITY> items = getRepo().findAll(); List<ENTITY> list = new ArrayList<ENTITY>(); for (ENTITY item : items) { list.add(item); } return list; } @Transactional(readOnly=true) @RequestMapping(value="/dump", method=RequestMethod.GET) public void dump(OutputStream response) throws IOException { Iterable<ENTITY> items = getRepo().findAll(); XStream xs = new XStream(); for (ENTITY item : items) { xs.toXML(item, response); } } @Transactional(readOnly = true) protected ArrayList<String> getOptionsForField(String attrName) { // TODO: push this in as getOptionsForField(String) return attrDefs.getEnumDefForAttrId(attrDefs.getAttrId(attrName)).getValues(); } protected void doSearch( FuzzyRepository<ENTITY,String> repo, Model model, String style, String ref, Pageable pageable, ENTITY idealMatch) { // requested match style int maxResults = pageable.getOffset() + pageable.getPageSize(); AttributeMatchQuery<ENTITY> query = new SubjectMatchQuery<ENTITY>(idealMatch, style, maxResults); // Do the actual query Page<Result<ENTITY>> results = repo.findMatchesFor(query, pageable); // Stick 'em in our model for our view to render model.addAttribute("subject", idealMatch.toString() == null ? "search" : idealMatch.toString()); model.addAttribute("ref", ref); model.addAttribute("results", results.getContent()); model.addAttribute("style", style); model.addAttribute("startNextPage", results.hasNextPage() ? maxResults : -1); model.addAttribute("pageSize", results.getSize()); } }