package com.hazelcast.samples.spring.data.chemistry.service;
import com.hazelcast.samples.spring.data.chemistry.Constants;
import com.hazelcast.samples.spring.data.chemistry.domain.Element;
import com.hazelcast.samples.spring.data.chemistry.domain.Isotope;
import com.hazelcast.samples.spring.data.chemistry.domain.IsotopeKey;
import com.hazelcast.samples.spring.data.chemistry.repository.ElementRepository;
import com.hazelcast.samples.spring.data.chemistry.repository.IsotopeRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
/**
* The chemistry service class hides that elements and isotopes are stored
* in different key spaces.
*/
@Service
@Slf4j
public class ChemistryService {
@Resource
private ElementRepository elementRepository;
@Resource
private IsotopeRepository isotopeRepository;
/**
* Indicate how many data items are stored.
*
* Although the figures give a breakdown for elements and
* isotopes, this doesn't confirm or deny if they are stored
* together or separately
*
* @return Entry counts for each type
*/
public Map<String, Long> count() {
Map<String, Long> result = new HashMap<String, Long>();
result.put(Constants.KEYSPACE_ELEMENT, this.elementRepository.count());
result.put(Constants.KEYSPACE_ISOTOPE, this.isotopeRepository.count());
return result;
}
/**
* Find all atomic weight values, part of the composite
* key for an {@link Isotope}.
*
* @return Unique atomic weights, in ascending sequence
*/
public Set<Integer> findAtomicWeights() {
Set<Integer> result = new TreeSet<Integer>();
for (Isotope isotope : this.isotopeRepository.findAll()) {
result.add(isotope.getIsotopeKey().getAtomicWeight());
}
return result;
}
/**
* Search the {@code element} key space for those with the
* required value for {@code element.getGroup()}.
*
* @param group a column in the periodic table
* @return Elements in that group
*/
public List<Element> findElementsByGroupSorted(final int group) {
return this.elementRepository.findByGroupOrderBySymbolDesc(group);
}
/**
* Find matches for an atomic weight.
*
* This example only considers the atomic weight to be made
* from protons (atomic number) plus neutrons, discounting other
* particles. Elements with different numbers of protons can
* still have the same total weight depending how many neutrons
* they have.
*
* @param weight to search on
* @return A possibly empty list of elements
*/
public List<Element> findElementsByAtomicWeight(final int weight) {
ArrayList<Element> result = new ArrayList<Element>();
for (Isotope isotope : this.isotopeRepository.findByIsotopeKeyAtomicWeight(weight)) {
result.add(this.elementRepository.findOne(isotope.getIsotopeKey().getSymbol()));
}
return result;
}
/**
* Return the number of neutrons for each isotopes, the
* atomic weight minus the number of protons.
*
* @return For each symbol, the variants in the number of neutrons
*/
public Map<String, Set<Integer>> neutrons() {
Map<String, Set<Integer>> result = new HashMap<String, Set<Integer>>();
for (Element element : this.elementRepository.findAll()) {
log.trace("neutrons(): {}", element);
String symbol = element.getSymbol();
Set<Integer> neutrons = new TreeSet<Integer>();
// Search isotopeRepository on part of the isotope key
for (Isotope isotope : this.isotopeRepository.findByIsotopeKeySymbol(symbol)) {
log.trace("neutrons(): {}", isotope);
neutrons.add(isotope.getIsotopeKey().getAtomicWeight() - element.getAtomicNumber());
}
result.put(symbol, neutrons);
}
return result;
}
/**
* Loads the test data.
*
* The logic here uses the exactness of the atomic weight. If it is
* whole number assume the element exists with only one form (one isotope).
* If the atomic weight is not a whole number, there must at least be
* one isotope lighter and one heavier.
*
* @return How many inserts were done, elements plus isotopes
*/
public int load() {
int count = 0;
// Atomic number, Symbol, Name, Group [optional], Period, Atomic Weight
for (String[] data : Constants.PERIODIC_TABLE) {
try {
Element element = new Element();
element.setAtomicNumber(Integer.parseInt(data[0]));
element.setSymbol(data[1]);
element.setName(data[2]);
element.setGroup("".equals(data[3]) ? null : Integer.parseInt(data[3]));
element.setPeriod(Integer.parseInt(data[4]));
log.trace("load(): {}", element);
this.elementRepository.save(element);
count++;
double averageWeight = Double.parseDouble(data[5]);
for (Double atomicWeight = Math.floor(averageWeight); atomicWeight <= Math.ceil(averageWeight); atomicWeight++) {
Isotope isotope = new Isotope();
IsotopeKey isotopeKey = new IsotopeKey();
isotopeKey.setSymbol(element.getSymbol());
isotopeKey.setAtomicWeight(atomicWeight.intValue());
isotope.setIsotopeKey(isotopeKey);
log.trace("load(): {}", isotope);
this.isotopeRepository.save(isotope);
count++;
}
} catch (Exception exception) {
log.error(data.toString(), exception);
}
}
return count;
}
/**
* Removes some or all of the test data.
*
* @param onlyIsotopes Isotope or (isotopes and elements)
*/
public void unload(boolean onlyIsotopes) {
this.isotopeRepository.deleteAll();
if (!onlyIsotopes) {
this.elementRepository.deleteAll();
}
}
}