SorcerersCave004.java - JDK 8

  • enums
  • JComboBox
  • BiFunction
  • sorting/Comparator
This display is slightly different than the code:
  • top and bottom panels are exchanged
  • the bottom panel is using a grid layout

Links:

References:

Notes:


// File: SorcerersCave004.java
// Date: Jun 21, 2013
// Author: Nicholas Duchon
// Purpose: demonstrate the development of a project -
//    in this case, the Sorcerer's Cave project
// History:
//    Sep 18, 2015 - added code to search:
//        String SorcerersCave004.search (String, String)
//        String Cave    .searchName (String)
//        String Party   .searchName (String)
//        String Creature.searchName (String)
//        >>> See commentary at end of SorcerersCave003.java
//    Feb 13, 2016 - experiment with lambda expressions
//    Feb 19, 2016 - cleaned up the comments, etc

import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.Font;

import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.JScrollPane;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JComboBox;

import java.util.ArrayList;
import java.util.Comparator;

public class SorcerersCave004 extends JFrame {
   static final long serialVersionUID = 123L;
  
   JTextArea jta = new JTextArea (35, 10); // rows, cols
   JComboBox <Comparator<?>> jcb2 = new JComboBox <> ();

   Cave cave = new Cave ();

   public SorcerersCave004 () {
      jta.setFont (new Font (Font.MONOSPACED, Font.PLAIN, 14));
      JScrollPane jsp = new JScrollPane (jta);
      add (jsp, BorderLayout.CENTER);
     
      JButton jbr = new JButton ("Populate");
      JButton jbd = new JButton ("Display");
      JButton jbc = new JButton ("Clear");
      JButton jbs = new JButton ("Search");
     
      JLabel     jls = new JLabel ("Search target");
      JTextField jtf = new JTextField (10);
     
      JComboBox <String> jcb = new JComboBox <String> ();
      jcb.addItem ("Index");
      jcb.addItem ("Type");
      jcb.addItem ("Name");
     
      JPanel jp = new JPanel ();
      jp.add (jbr); // Populate
      jp.add (jbd); // Display
      jp.add (jbc); // Clear
      jp.add (jls); // label: Search Target
      jp.add (jtf); // text field - search input
      jp.add (jcb); // combo box
      jp.add (jbs); // Search
     
      add (jp, BorderLayout.PAGE_START);
     
      for (Creature.Sorts o: Creature.Sorts.values())
         jcb2.addItem (o);
      for (Treasure.Sorts o: Treasure.Sorts.values())
         jcb2.addItem (o);
     
      jp = new JPanel ();
      jp.add (jcb2);
      add (jp, BorderLayout.PAGE_END);
     
      setTitle ("Sorcerer's Cave");
      setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
      pack ();
      setLocationRelativeTo (null);
      setVisible (true);
     
      jbr.addActionListener (e -> readFile());
      jbd.addActionListener (e -> displayCave ());
      jbc.addActionListener (e -> cave = new Cave ());
      jbs.addActionListener (e -> search ((String)(jcb.getSelectedItem()), jtf.getText()));
     
      jcb2.addItemListener (this::sortA);
  } // end no-parameter constructor
  
   public void readFile () {
      jta.append ("Read File button pressed\n");
     
      Party p; Creature c;
      cave.parties.add (p = new Party ("party A", 1));
      p.members  .add (c = new Creature ("CA", 2));
      c.artifacts.add (new Artifact ("art aa", 3));
      c.artifacts.add (new Artifact ("art ab", 4));
      c.artifacts.add (new Artifact ("art ac", 5));
      c.treasures.add (new Treasure ("trs ta", 6));
      c.treasures.add (new Treasure ("trs tb", 6));
      c.treasures.add (new Treasure ("trs tc", 6));
      c.treasures.add (new Treasure ("trs td", 6));
      c.treasures.add (new Treasure ("trs te", 6));
      p.members  .add (c = new Creature ("CB", 9));
      p.members  .add (c = new Creature ("CC", 8));
      p.members  .add (c = new Creature ("CD", 7));
  
} // end method readFile
  
   public void displayCave () {
      jta.setText ("Display Cave button pressed\n");
      jta.append (cave.toString());
  
} // end method readFile
  
   public void search (String type, String target) {
      jta.setText (String.format ("Search button pressed, type: >%s<, target: >%s<\n", type, target));
      jta.append (cave.searchName (target));
  
} // end method readFile
  
   public void sortA (java.awt.event.ItemEvent e) {
      if (e.getStateChange() != java.awt.event.ItemEvent.SELECTED)
         return;
      Comparator <?> ca = jcb2.getItemAt (jcb2.getSelectedIndex());
      if (ca instanceof Creature.Sorts)
         for (Party p: cave.parties)
            p.members.sort ((Creature.Sorts)ca);
      else
         for (Party p: cave.parties)
            for (Creature c: p.members)
               c.treasures.sort ((Treasure.Sorts)ca);
      jta.setText (cave.toString());
  
} // end meethod sortA
  
   public static void main (String [] args) {
      SorcerersCave004 sc = new SorcerersCave004 ();
  
} // end main
} // end class SorcerersCave

class Cave {
   public static java.util.Random rn = new java.util.Random ();
   ArrayList <Party>       parties = new ArrayList <> ();
   ArrayList <CaveElement> stuff   = new ArrayList <> ();
  
   public String searchName (String target) {
      String st = "";
      for (Party p: parties) st += p.searchName (target) + "\n";
      return st;
  
} //end method searchName
  
   public String toString () {
      String st = "Cave.toString:\nThe Parties\n";
      for (Party p: parties)
         st += p + "\n";
      st += "+++++++\nThe unassociated stuff:\n";
      for (CaveElement e: stuff)
         st += e + "\n";
      return st;
  
} // end toString method
} // end class Cave

class CaveElement {
   String name = ""; // make sure we have a default value here, NOT null
   int index = 0;
   String indentA = String.format ("%3s", "");
   String indentB = String.format ("%5s", "");
   String indentC = String.format ("%7s", "");
   String indentD = String.format ("%9s", "");
   String indentE = String.format ("%11s", "");
  
   CaveElement (String n, int k) {name = n; index = k;
}   

   public String toString () {
      return name + " " + index;
  
} // end method toString
}
// end class CaveElement

class Party extends CaveElement {
   ArrayList <Creature> members = new ArrayList <> ();
  
   public Party (String n, int k) {super (n, k);
}
  
   public String searchName (String target) {
      String st = "";
      if (target.equalsIgnoreCase (name)) st += this + "\n";
      for (Creature c: members) st += c.searchName (target) + "\n";
      return st;
  
} // end searchName
  
   public String toString () {
      String st = super.toString () + "\n" + indentA + "Members:\n";
      for (Creature c: members)
         st += indentB + c;
      return st;
  
} // end method toString
}
// end class CaveElement

// enum below (Creature and Treasure) can only implement interface, not extend a class
// thus, interface Sortable <T> could work
// except that all the variables in an interface are final
// which kind of defeats the idea of moving much of the common code
//   in Creature.Sorts and Treasure.Sorts to here

// interface Sortable <T> extends Comparator <T> {
//   String display = ""; // must be given an initial value
//   java.util.function.BiFunction <Creature, Creature, Integer> func = null; // compiles but cannot be change!?!?!
//  
//   default public int compare (T a, T b) {return 0;}
// } // end class sortable

class Creature extends CaveElement {
   enum Sorts implements Comparator <Creature> {
      NAME     ("Name"             , (a,b) -> a.name.compareTo(b.name)),
      EMPATHY  ("Empathy"          , (a,b) -> a.empathy  - b.empathy),
      FEAR     ("Fear"             , (a,b) -> a.fear     - b.fear),
      CAPACITY ("Carrying Capacity", (a,b) -> a.capacity - b.capacity),
      AGE      ("Age"              , (a,b) -> Double.compare (a.age   , b.age)),
      HEIGHT   ("Height"           , (a,b) -> Double.compare (a.height, b.height)),
      WEIGHT   ("Weight"           , (a,b) -> Double.compare (a.weight, b.weight));
      
      String display;
      java.util.function.BiFunction <Creature, Creature, Integer> func; //  = (a,b) -> a.fear - b.fear;
      
      Sorts (String s) {display = "Sort Creatures by: " + s;
}  // String constructor
      Sorts (String s, java.util.function.BiFunction <Creature, Creature, Integer> bf) {
         this (s);
         func = bf;
     
} // end 2 parameter constructor
      public String toString ()  {return display;} // to get the display in the JComboBox right
      public int compare (Creature c1, Creature c2) {return func.apply (c1, c2);}
   }; // end enum

   ArrayList <Treasure> treasures = new ArrayList <> ();
   ArrayList <Artifact> artifacts = new ArrayList <> ();
  
   int empathy   = Cave.rn.nextInt (100);
   int fear      = Cave.rn.nextInt (100) + 200;
   int capacity  = Cave.rn.nextInt (100) + 500;
   double age    = Cave.rn.nextDouble ()*100 +  10;
   double height = Cave.rn.nextDouble ()* 50 + 100;
   double weight = Cave.rn.nextDouble ()*100 +  50;

   public Creature (String n, int k) {super (n, k);}
  
   public String searchName (String target) {
      String st = "";
      if (target.equalsIgnoreCase (name)) st += this + "\n";
      return st;
   } // end searchName
  
   public String toString () {
      String st = super.toString() + " values: ";
      st += String.format ("e:%d f:%d c:%d a:%.2f h:%.2f w:%.2f\n", empathy, fear, capacity, age, height, weight);
      st += indentC + "Artifacts:\n";
      for (Artifact a: artifacts)
         st += indentD + a + "\n";
      st += indentC + "Treasures:\n";
      for (Treasure t: treasures)
         st += indentD + t + "\n";
      return st;
   } // end method toString

}
// end class Creature

class Artifact extends CaveElement {

   public Artifact (String n, int k) {super (n, k);}
  

}
// end class Artifact

class Treasure extends CaveElement {
   enum Sorts implements Comparator <Treasure> {
      NAME   ("Name"  , (a,b) -> a.name.compareTo(b.name)),
      VALUE  ("Value" , (a,b) -> a.value   - b.value),
      WEIGHT ("Weight", (a,b) -> Double.compare (a.weight, b.weight));
      
      String display;
      java.util.function.BiFunction <Treasure, Treasure, Integer> func; //  = (a,b) -> a.fear - b.fear;
      
      Sorts (String s) {display = "Sort Treasures by: " + s;
} // String constructor
      Sorts (String s, java.util.function.BiFunction <Treasure, Treasure, Integer> bf) {
         this (s);
         func = bf;
     
} // end 2 parameter constructor
      public String toString () {
         return display;
} // to get the display in the JComboBox right
      public int compare (Treasure t1, Treasure t2) {
         return func.apply (t1, t2);
}
   }; // end enum

   double weight = Cave.rn.nextDouble ()*200 +  5;
   int    value  = Cave.rn.nextInt    (30);

   public Treasure (String n, int k) {super (n, k);
}
  
   public String toString () {
      String st = "";
      st += super.toString () + " values: ";
      st += String.format ("w:%6.2f v:%d", weight, value);
      return st;
  
} // end method toString
}
// end class Treasure


So it can work like this:

In SorcerersCave (or whatever holds an instance of the Cave class, the class that has ArrayList <Party> parties):

   public void search (String type, String target) {
      jta.setText (String.format ("Search button pressed, type: >%s<, target: >%s<\n",
         type, target));
      jta.append (cave.searchName (target));
   } // end method search

So this method calls searchName (String) in the Cave class:

   public String searchName (String target) {
    String st = "";
    for (Party p: parties) st += p.searchName (target) + "\n";
    return st;
   } //end method searchName

Which in turn calls the searchName (String) in the Party class:

   public String searchName (String target) {
      String st = "";
      if (target.equalsIgnoreCase (name)) st += this + "\n";
      for (Creature c: members) st += c.searchName (target) + "\n";
      return st;
   } // end searchName

Which in turn calls searchName (String) in the Creature class:

   public String searchName (String target) {
    String st = "";
    if (target.equalsIgnoreCase (name)) st += this + "\n";
    return st;
   } // end searchName

Which should call the searchName methods in the Treasure and Artifact classes.

And then you want to modify this approach in one way or another to handle searching by type and by index.

I think you get the idea from these code fragments.

>>>>>> One final thought - you could do something very similar if you were interested in returning the references to each object - in which case you might want to return a List <CaveElement>
from each search method, for example.



By: Nicholas Duchon.