For a project, which generates XML-files based on a XSD-file, I want to automatically generate the documentation. *
In this documentation I list the different elements defined in the XSD.
And for each element I want to show an example of that element.
The problem is, that the XML-example might be quite long and contains a lot of children.
Therefore I want to shorten the example by:
limiting the shown depth
limiting the amount of elements in a list
For the root-element that example might look like the following:
<root>
<elements>
<element>...<element>
<element>...<element>
<element>...<element>
...
</elements>
</root>
My approach:
To generate classes from the XSD and to generate and validate the XML files I use JAXB.
But I could not figure out how to marshal a Non-Root element.
Therefore I am generating my examples with XStream.
To limit the XML-example I am trying to decorate the PrettyPrintWriter, but that seems to be quite cumbersome.
The two decorators can be seen in my answer.
I just did not expect to care about the internals of a library for such a (common?) feature.
Is there an easier way to do this? (I can also use another library than XStream, or none at all.)
*
My approach is influenced by Spring Auto Rest Docs
To limit the shown depth I created the following XStream WriterWrapper. The class can wrap for example a PrettyPrintWriter and ensures that the wrapped writer only receives the nodes above a given depth threshold.
public class RestrictedPrettyPrintWriter extends WriterWrapper {
private final ConverterLookup converterLookup;
private final int maximalDepth;
private int depth;
public RestrictedPrettyPrintWriter(HierarchicalStreamWriter sw, ConverterLookup converterLookup, int maximalDepth) {
super(sw);
this.converterLookup = converterLookup;
this.maximalDepth = maximalDepth;
}
#Override public void startNode(String name, Class clazz) {
Converter converter = this.converterLookup.lookupConverterForType(clazz);
boolean isSimpleType = converter instanceof SingleValueConverter;
_startNode(name, !isSimpleType);
}
#Override public void startNode(String name) {
_startNode(name, false);
}
#Override public void endNode() {
if (_isLessDeepThanMaximalDepth() || _isMaximalDepthReached()) {
super.endNode();
}
depth--;
}
#Override public void addAttribute(String key, String value) {
if (_isLessDeepThanMaximalDepth() || _isMaximalDepthReached()) {
super.addAttribute(key, value);
}
}
#Override public void setValue(String text) {
if (_isLessDeepThanMaximalDepth() || _isMaximalDepthReached()) {
super.setValue(text);
}
}
/**
* #param name name of the new node
* #param isComplexType indicates if the element is complex or contains a single value
*/
private void _startNode(String name, boolean isComplexType) {
depth++;
if (_isLessDeepThanMaximalDepth()) {
super.startNode(name);
} else if (_isMaximalDepthReached()) {
super.startNode(name);
/*
* set the placeholder value now
* setValue() will never be called for complex types
*/
if (isComplexType) {
super.setValue("...");
}
}
}
private boolean _isMaximalDepthReached() {
return depth == maximalDepth;
}
private boolean _isLessDeepThanMaximalDepth() {
return depth < maximalDepth;
}
}
To limit the lists, I tried, in a first attempt, to modify the XStream CollectionConverter. But this approach was not general enough because implicit lists do not use this converter.
Therefore I created another WriterWrapper which counts the consecutive occurrences of elements with the same name.
public class RestrictedCollectionWriter extends WriterWrapper {
private final int maxConsecutiveOccurences;
private int depth;
/** Contains one element per depth.
* More precisely: the current element and its parents.
*/
private Map < Integer, Elements > elements = new HashMap < > ();
public RestrictedCollectionWriter(HierarchicalStreamWriter sw, int maxConsecutiveOccurences) {
super(sw);
this.maxConsecutiveOccurences = maxConsecutiveOccurences;
}
#Override public void startNode(String name, Class clazz) {
_startNode(name);
}
#Override public void startNode(String name) {
_startNode(name);
}
#Override public void endNode() {
if (_isCurrentElementPrintable()) {
super.endNode();
}
depth--;
}
#Override public void addAttribute(String key, String value) {
if (_isCurrentElementPrintable()) {
super.addAttribute(key, value);
}
}
#Override public void setValue(String text) {
if (_isCurrentElementPrintable()) {
super.setValue(text);
}
}
/**
* #param name name of the new node
*/
private void _startNode(String name) {
depth++;
Elements currentElement = this.elements.getOrDefault(depth, new Elements());
this.elements.put(depth, currentElement);
Elements parent = this.elements.get(depth - 1);
boolean parentPrintable = parent == null ? true : parent.isPrintable();
currentElement.setName(name, parentPrintable);
if (currentElement.isPrintable()) {
super.startNode(name);
}
}
private boolean _isCurrentElementPrintable() {
Elements currentElement = this.elements.get(depth);
return currentElement.isPrintable();
}
/**
* Evaluates if an element is printable or not.
* This is based on the concurrent occurences of the element's name
* and if the parent element is printable or not.
*/
private class Elements {
private String name = "";
private int concurrentOccurences = 0;
private boolean parentPrintable;
public void setName(String name, boolean parentPrintable) {
if (this.name.equals(name)) {
concurrentOccurences++;
} else {
concurrentOccurences = 1;
}
this.name = name;
this.parentPrintable = parentPrintable;
}
public boolean isPrintable() {
return parentPrintable && concurrentOccurences <= maxConsecutiveOccurences;
}
}
}
The following listing shows, how the two classes can be used.
XStream xstream = new XStream(new StaxDriver());
StringWriter sw = new StringWriter();
PrettyPrintWriter pw = new PrettyPrintWriter(sw);
RestrictedCollectionWriter cw = new RestrictedCollectionWriter(pw, 3);
xstream.marshal(objectToMarshal, new RestrictedPrettyPrintWriter(cw, xstream.getConverterLookup(), 3));
Related
I have below table
question_text_id question_id language_id question_text
2 7 1 english_text_1
3 7 2 spanish_text_1
4 8 2 spanish_text_2
5 8 1 english_text_2
NOw i want to create list for each distinct question_id
i have used below code
List<QuestionText> questionTextList = questionManager.getQuestionsTextByQuestionId(Long.parseLong(questions.getQuestionId().toString()));
for (QuestionText questionText : questionTextList) {
questionMap.put("questionId", questionText.getQuestionId());
questionMap.put("language", questionText.getLanguageId());
if(questionText.getLanguageId().longValue() == 1){
questionMap.put("englishQuestionText",questionText.getQuestionText());
} else {
questionMap.put("spanishQuestionText",questionText.getQuestionText());
}
questionListMap.add(questionMap);
}
adminCollectionBookendModel.put("questionListMap",questionListMap);
[{questionId = 1,language=1, englishQuestionText = english_text_1,spanishQuestionText=spanish_text_1},{questionId = 1,language=2, englishQuestionText = english_text_1,spanishQuestionText=spanish_text_1}]
But this give me repeatetion of object of same data if i have both spanish and english question text as shown above. How to get unique list?
How to get both spanish text and english text for each question_id along with language_id and to access it?
Please help me on this
The first step would be to create a POJO Class like this,
public class QuestionDetails {
private int questionId;
private int englishLanguageId;
private int spanishLanguageId;
private String englishLanguageText;
private String spanishLanguageText;
public int getQuestionId() {
return questionId;
}
public void setQuestionId(int questionId) {
this.questionId = questionId;
}
public int getEnglishLanguageId() {
return englishLanguageId;
}
public void setEnglishLanguageId(int englishLanguageId) {
this.englishLanguageId = englishLanguageId;
}
public int getSpanishLanguageId() {
return spanishLanguageId;
}
public void setSpanishLanguageId(int spanishLanguageId) {
this.spanishLanguageId = spanishLanguageId;
}
public String getEnglishLanguageText() {
return englishLanguageText;
}
public void setEnglishLanguageText(String englishLanguageText) {
this.englishLanguageText = englishLanguageText;
}
public String getSpanishLanguageText() {
return spanishLanguageText;
}
public void setSpanishLanguageText(String spanishLanguageText) {
this.spanishLanguageText = spanishLanguageText;
}
#Override
public String toString() {
return new StringBuilder().append("questionId: ").append(questionId)
.append(" ,englishLanguageId: ").append(englishLanguageId)
.append(" ,englishLanguageText: ").append(englishLanguageText)
.append(" ,spanishLanguageId: ").append(spanishLanguageId)
.append(" ,spanishLanguageText: ").append(spanishLanguageText)
.toString();
}
}
Next step would be to change your code snippet like this:
List<QuestionDetails> questionsList = new ArrayList<>();
List<QuestionText> questionTextList = questionManager
.getQuestionsTextByQuestionId(Long.parseLong(questions
.getQuestionId().toString()));
for (QuestionText questionText : questionTextList) {
/* Get QuestionDetails Object */
QuestionDetails qd = getQuestionDetails(
questionText.getQuestionId(), questionsList);
/* Check Null */
if(null == qd) {
/* Get New Object */
qd = new QuestionDetails();
/* Add Object To List */
questionsList.add(qd);
}
/* Set Question ID */
qd.setQuestionId(questionText.getQuestionId());
/* Set Language ID & Text */
if (questionText.getLanguageId().longValue() == 1) {
qd.setEnglishLanguageId(questionText.getLanguageId()
.longValue());
qd.setEnglishLanguageText(questionText.getQuestionText());
} else {
qd.setSpanishLanguageId(questionText.getLanguageId()
.longValue());
qd.setSpanishLanguageText(questionText.getQuestionText());
}
}
adminCollectionBookendModel.put("questionListMap", questionsList);
Finally, here is the implementation of the getQuestionDetails function:
private QuestionDetails getQuestionDetails(int questionId,
List<QuestionDetails> questionsList) {
/* Search Existing Object */
for (QuestionDetails qd : questionsList) {
/* Match Found */
if (qd.getQuestionId() == questionId) {
return qd;
}
}
/* No Object Found */
return null;
}
I would recommend doing the following:
To distinguish QuestionText, you need to override equals method using question_text_id. Otherwise two questions with the same question_id, but different language texts would be equal.
Then create two separate maps for each language. Then just iterate throug all questions and put each question in a corresponding map by question_id. You can retrive a question object by its question_id and get all necessary fields from it, including spanish / english text
List<QuestionText> questionTextList = questionManager.getQuestionsTextByQuestionId(Long.parseLong(questions.getQuestionTextId().toString()));
for (QuestionText questionText : questionTextList) {
if(questionText.getLanguageId().longValue() == 1){
englishQuestionMap.put("question_id",questionText);
} else {
spanishQuestionMap.put("question_id",questionText);
}
questionListMap.add(questionMap);
}
So, your maps will have type of Map<Long, QuestionText>
I have an interface namely Medicine and I created few instances for that. let's have a look,
interface Medicine {
Medicine Antibiotic = new Medicine() {
#Override
public int getCountOfTuberculous(QuarantineTwo quarantineTwo) {
return quarantineTwo.tuberculous().getSize();
}
/**
* Antibiotic cures the tuberculous
*
* #param q
*/
#Override
public void on(QuarantineTwo q) {
int initialNumOfTuberculous = getCountOfTuberculous(q);
System.out.println("Numbe of perople have Tuberculous before treated w/ Antibiotic = " + initialNumOfTuberculous);
q.tuberculous().changeHealthStatus(q.healthy());
}
#Override
public Treatment combine(Treatment treatment) {
return treatment.plus(this);
}
#Override
public String toString() {
return "Antibiotic";
}
};
Medicine Insulin = new Medicine() {
// cant use this method as it will provide the number of Tuberculous 0
// because, initially, the Quarantine was treated with Antibiotic
#Override
public int getCountOfTuberculous(QuarantineTwo quarantineTwo) {
return quarantineTwo.tuberculous().getSize();
}
#Override
public void on(QuarantineTwo q) {
if (isInsulinCombinedWithAntibiotic(q.getTreatment())) {
q.healthy().changeHealthStatus(q.feverish());
// q.healthy().changeHealthStatus(q.feverish(), iniNumOfTuberculous);
} else {
// Prevent None effects, done is this.combine
}
}
#Override
public Treatment combine(Treatment treatment) {
return treatment.remove(Medicine.None)
.plus(this);
}
/**
* helper method to see whether the Insulin is combined with Antibiotic
*
* #param treatment
* #return
*/
private boolean isInsulinCombinedWithAntibiotic(Treatment treatment) {
return treatment.contains(this) &&
treatment.contains(Medicine.Antibiotic);
}
#Override
public String toString() {
return "Insulin";
}
};
void on(QuarantineTwo quarantineTwo);
Treatment combine(Treatment treatment);
int getCountOfTuberculous(QuarantineTwo quarantineTwo);
}
Now, when I'm testing I may call like this,
#Test
public void antibioticPlusInsulin() throws Exception {
quarantine.antibiotic();
quarantine.insulin();
assertEquals("F:3 H:1 D:3 T:0 X:0", quarantine.report());
}
The two lines of codes means that we combined the treatment procedures with both the antibiotic and insulin to the Quarantine system and affect should be accumulative.
quarantine.antibiotic();
quarantine.insulin();
And, hence, I would like to keep a track of how many people are cured with Antibiotic initially from the Tuberculous stored in the initialNumOfTuberculous and use that value to make the call
q.healthy().changeHealthStatus(q.feverish(), iniNumOfTuberculous);
This call suppose to change the all the people from healthy state to feverish but the ones initially cured with Tuberculous.
How to store the value of the iniNumOfTuberculous inside the Medicine Antibiotic and make it available in the Medicine Insulin ?
Sounds like you need an abstract class
abstract class AbstractMedicine implements Medicine {
protected int iniNumOfTuberculous;
}
public class Insulin extends AbstractMedicine {
// can use iniNumOfTuberculous here
}
Note: The availability of the variable definition is shared; the value itself is not.
I don't think you should implement your concrete classes inside an interface, by the way
I need some help on my class design or better said a reference to a common design pattern for a problem.
I am working in the aircraft industry. So far my programming skills are VBA and basic JAVA applications.
As an engineer my task is to create CAD Models for fixating components in and on to aircraft kitchens. To ensure a high reusability and to reduce development time I want to create a program which can recommend previous solutions.
Basically each aircraft operator can select from a catalog which galleys/kitchens (Monument) it would like to have installed. Inside these Monuments are multiple compartments. Inside a compartment we can install multiple equipment’s/components.
I would like to write a program which can tell me "you have installed these components together before -> In this compartment -> in that aircraft for that customer"
I have modeled the compartment, the monuments, and the aircraft. Each class extends form the same class BaseHolder:
public abstract class BaseHolder <I> {
private final ArrayList <I> heldItems = new ArrayList<I>();
public boolean addItem(final I i){
Objects.requireNonNull(i, "cannot add NULL");
return heldItems.add(i);
}
public boolean removeItem(I i){
return heldItems.remove(i);
}
public boolean contains(I i){
return heldItems.contains(i);
}
public int itemCount(){
return heldItems.size();
}
public boolean isEmpty(){
return heldItems.isEmpty();
}
public void Clear() {
heldItems.clear();
}
protected List<I> getHeldItems(){
return heldItems;
}
public I getElement(int n){
return heldItems.get(n);
}
}
public class Aircraft extends BaseHolder<Monument> {
// code
}
public class Monument extends BaseHolder<Compartment> {
private String name;
public Monument (String name){
this.setName(name);
}
// code
#Override
public boolean addItem(final Compartment c) {
Objects.requireNonNull(c, "cannot add NULL");
if (contains (c) ){
throw new IllegalArgumentException("Compartment already added!");
};
for(Compartment ctmp : getHeldItems()){
if (ctmp.getName().equals(c.getName() ) ) {
throw new IllegalArgumentException("Compartment with an identical name already exits");
}
}
return getHeldItems().add(c);
}
public Compartment getCompartment(int n){
return getHeldItems().get(n);
}
public Compartment getCompartment(String name){
for(Compartment ctmp : getHeldItems()){
if (ctmp.getName().equals(name) ) {
return ctmp;
}
}
return null;
}
}
public class Compartment extends BaseHolder<IWeighable>{
private String name = "";
private double MAX_LOAD = 0.0;
public Compartment (String name ,final double max_load){
this.setName(name);
updateMaxLoad(max_load);
}
// code
protected double getTotalLoad(){
// code
}
/**
*
* #param load
* #throws InvalidParameterException if max load not >= than 0.0
*/
public void setMaxLoad(final double load){
if (load >= 0.0){
this.MAX_LOAD = load;
} else {
throw new InvalidParameterException("max load must be greater than 0.0");
}
}
public boolean isOverloaded(){
return (getTotalLoad() > MAX_LOAD ) ;
}
}
The problem I am having is that this design seems to have many flaws. Apart from it being rather tedious: getElement(n).getElement(n).getElement(n)
Adding elements to a compartment results in all aircrafts using the same compartment, having all the same equipment’s/components installed. As it is the same object in the DB. An instance of the compartment would be need. Cloning the DB Compartment before adding it to an aircraft is no option. I need to be able to change the allowable loads, a change it for all. To resolve this I thought of using some type of “wrapper” class as in:
public class MonumentManager {
public ArrayList <Monument> monuments = new ArrayList<>();
public ArrayList <LinkObect> links;
class LinkObect{
private Compartment c;
private IWeighable e;
LinkObect(Compartment c, IWeighable e){
this.c = c;
this.e = e;
}
}
public boolean addMonument(Monument m){
return monuments.add(m);
}
public void addElementToCompartment(IWeighable e, Compartment c){
boolean known = false; //to check if the passed compartment is known/handeld to/by the MonumentManager
for (Monument m : monuments){
if ( m.getCompartment(c.getName() ) != null ) known = true;
}
if (known){
links.add(new LinkObect(c, e));
} else {
throw new IllegalArgumentException("Compartment is not inside a managed Monument!");
}
}
public List<Compartment> whereUsed(IWeighable e){
// TODO
}
}
This class might solve the problem but it is feels odd. Can anybody point me in the right direction towards a common design pattern etc. I am reading a book from the local library on design patterns. But it seems to be slightly above me. (as is maybe my task).
Any suggestions / help etc would be highly appreciated.
I hope I'm understanding this correctly.
One thing is the Component you want to install that has certain characteristics and another thing is some representation of what you have installed.
The information of your installation does not need to be in your Component but in something else, let's call it Installation.
Your Installation has to know 2 things:
What kind of Component it is.
What other Installations it has inside.
The installation will look something like this.
public class Installation {
private Component type;
private List<Installation> content;
public Installation(Component type){
this.type = type;
this.content = new ArrayList<Component>();
}
//you can have methods for add, remove, etc...
}
Feel free to ask further clarifications.
The original reason for my Jaxb Question
JaxB reference resolving
was that I couldn't get the same issue working with the simple-framework:
http://old.nabble.com/Two-Phase-support-for-CycleStrategy--td34802791.html
Today I got the things working with a persister call back to the same point as in my Jaxb Questions:
I get copies - not references. Again I am looking for a solution with proper references. This time for the Simple XML framework.
The example here has the base class "ModelElement" not Person as in the other question. Otherwise the problem is the same.
Again I am calling the unmarshalling twice to get all ids in PASS 1 and use the gathered results from the lookup HashMap created in PASS2.
What would be a solution to get proper references? My assumption is that adding a call back that actually lets the called function modify the unmarshalling result (see How to use an output parameter in Java? for a wrapping approach)
would do the trick (comparable to the JaxB solution I have posted in the meantime).
Persister serializer = new Persister();
ModelElementSimpleXmlImpl.lookup.clear();
serializer.read(result, xml);
System.err.println("PASS 2");
serializer.read(result, xml);
This code is from the ModelElementSimpleXmlImpl base class:
...
protected String ref;
/**
* getter for xsd:string/String id
* #return id
*/
#org.simpleframework.xml.Attribute(name="ref",required=false)
public String getRef() {
return ref;
}
/**
* setter for xsd:string/String id
* #param pid - new value for id
*/
#org.simpleframework.xml.Attribute(name="ref",required=false)
public void setRef(String pRef) {
ref=pRef;
}
private boolean debug=true;
/**
* show debug information
* #param title
* #param key
* #param me
* #param found
*/
public void showDebug(String title,String key,ModelElementSimpleXmlImpl me, ModelElementSimpleXmlImpl found) {
String deref="?";
if (found!=null)
deref="->"+found.getId()+"("+found.getClass().getSimpleName()+")";
if (debug)
System.err.println(title+": "+key+"("+me.getClass().getSimpleName()+")"+deref+" - "+this);
}
/**
* keep track of the elements already seen
*/
public static Map<String,ModelElementSimpleXmlImpl> lookup=new HashMap<String,ModelElementSimpleXmlImpl>();
#Validate
public void validate() {
ModelElementSimpleXmlImpl me=this;
String key=me.getId();
if (key!=null) {
showDebug("id",key,me,null);
lookup.put(key, me);
}
key=me.getRef();
if (key!=null) {
if (lookup.containsKey(key)) {
ModelElementSimpleXmlImpl meRef=lookup.get(key);
showDebug("ref",key,me,meRef);
me.setRef(null);
me.copyFrom(meRef);
} else {
if (debug)
showDebug("ref",me.getRef(),me,null);
}
}
}
Niall Gallagher suggested:
Should be fairly easy to do with something like the CycleStrategy. Just create MyCycleStrategy and where there is an exception with "Invalid reference" just return null and remember the reference. When you have picked up all the ids and the values then do a second pass. In the second pass assign the value to the first occurrence of either the ref or the id. Then all following references should be given the same value. This should work.
And he is right. The following extended Cycle Strategy works as proposed:
/**
* Two Path Cycle Strategy
*
* #author wf
*
*/
public static class TwoPathCycleStrategy extends CycleStrategy {
String id;
String ref;
public static boolean debug = false;
/**
* create a twoPath Cycle Strategy
*
* #param id
* #param ref
*/
public TwoPathCycleStrategy(String id, String ref) {
super(id, ref);
this.id = id;
this.ref = ref;
}
/**
* show debug information
*
* #param title
* #param key
* #param value
*/
public void showDebug(String title, String key, Value value) {
if (debug) {
String id = "?";
Object v = value;
while ((v instanceof Value) && ((Value) v).isReference()) {
v = ((Value) v).getValue();
}
if (v == null) {
id = "null";
} else {
// FIXME - adapt to your code or comment out
//if (v instanceof ModelElement) {
// ModelElement me = (ModelElement) v;
// id = me.getId();
//}
}
System.err.println(title + ":" + key + "->"
+ v.getClass().getSimpleName() + ":"
+ value.getType().getSimpleName() + ":" + value.isReference() + ":"
+ id);
}
}
public Map<String, Value> lookup = new HashMap<String, Value>();
#SuppressWarnings("rawtypes")
#Override
public Value read(Type type, NodeMap node, Map map) throws Exception {
Value value = null;
Node refNode = node.get(ref);
Node keyNode = node.get(id);
try {
value = super.read(type, node, map);
if (keyNode != null) {
String key = keyNode.getValue();
if (value != null) {
showDebug("id", key, value);
lookup.put(key, value);
} else {
showDebug("id?", key, value);
}
}
} catch (CycleException ce) {
if (ce.getMessage().contains("Invalid reference")) {
if (refNode != null) {
String key = refNode.getValue();
if (lookup.containsKey(key)) {
value = lookup.get(key);
showDebug("ref", key, value);
} else {
showDebug("ref?",key,null);
}
}
} else {
throw ce;
}
}
return value;
}
}
In Lucene, I am using the TaxonomyReader to read an index of a taxonomy stored in my disk. For a given category, I need to find all categories that are its children. However, in the Lucene's API I could find a method to retrieve the parent but not with the children. There is a method called getChildrenArrays() that returns a ChildrenArrays object. As you can see, this class only has two methods:
getYoungestChildArray
getOlderSiblingArray
I want to implement Enumerator using these two methods. Does someone know how to do it?
I got the following:
private class EnumChildren implements Enumeration<Integer> {
int category, current;
int[] next, first;
public EnumChildren(int category) {
ChildrenArrays childrenArrays = tr.getChildrenArrays();
first = childrenArrays.getYoungestChildArray();
next = childrenArrays.getOlderSiblingArray();
current = first[category];
}
public boolean hasChildren() {
return (current != TaxonomyReader.INVALID_ORDINAL);
}
#Override
public boolean hasMoreElements() {
current = next[current];
return (current != TaxonomyReader.INVALID_ORDINAL);
}
#Override
public Integer nextElement() {
return current;
}
}
Ans is used as:
ordinal = tr.getOrdinal(new CategoryPath(nodeCategory.getPath(), '/'));
EnumChildren childrenEnumeration = new EnumChildren(ordinal);
if (childrenEnumeration.hasChildren()) {
do {
int current = childrenEnumeration.nextElement();
Category child = new Category(tr.getPath(current).toString());
addChildren(child);
nodeCategory.children.add(child);
} while (childrenEnumeration.hasMoreElements());
}