2013년 8월 13일 화요일

[오라클자바community]JAVA에서 직접 구현한 스택, 스윙(Stack, Swing)을 이용한 GUI 계산기 소스

JAVA에서 직접 구현한 스택, 스윙(Stack, Swing)을 이용한 GUI 계산기 소스


오라클자바커뮤니티에서 설립한 오엔제이프로그래밍 실무교육센터
(신입사원채용무료교육, 오라클SQL, 튜닝, 힌트,자바프레임워크, 안드로이드, 아이폰, 닷넷)  


JAVA에서 직접 구현한 스택, 스윙(Stack, Swing)을 이용한 GUI 계산기 소스
1. ArrayStack.java
import java.util.*;
interface Stack {  
            public boolean isEmpty();
            public Object peek();
            public void push(Object theObject);
            public Object pop();
}

 public class ArrayStack implements Stack   {           
             int top;         // current top of stack
             Object [] stack;   // element array
              
             public ArrayStack(int initialCapacity) {
                  if (initialCapacity < 1)
                  throw new IllegalArgumentException
                                        ("initialCapacity must be >= 1");
                  stack = new Object [initialCapacity] ;
                  top = -1;
             }
     
       public ArrayStack() {
      this(10);
       }
      
       public boolean isEmpty( ) {
          return top == -1;
       }      
       
        public Object peek() {
               if (isEmpty() )
                     throw new EmptyStackException();
               return stack[top];
        }    

       
  public void push(Object theElement) {
    // increase array size if necessary   
    if (top == stack.length - 1) ensureCapacity();
        
            // put theElement at the top of the stack 
            stack[++top] = theElement;
      }

 
     public Object pop() {
            if  (isEmpty())
                  throw new EmptyStackException();
            Object topElement = stack[top];
             stack[top--] = null;   // enable garbage collection
             return topElement;
      }   
   private void ensureCapacity()  {
      Object[] larger = new Object[stack.length*2];
      for (int index=0; index < stack.length; index++)
         larger[index] = stack[index];
      stack = larger;
   }
   public String toString() {
    if (isEmpty())
      return "<empty stack>";
    String result = "<stack :";
    for (int i = top; i >= 0; i--)
      result += stack[i] + " ";
    return result + ">";
  } // end toString
}
 

2. MessageBox.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MessageBox extends JDialog implements ActionListener {
 public MessageBox(Frame parent, String title, String message) {
  super(parent, title, true);
  //대화상자를 내부에 위치시킴
  Dimension parentSize = parent.getSize();
  Point p = parent.getLocation();
  setLocation(p.x+parentSize.width/4, p.y+parentSize.height/4);
  //메시지 패널을 생성
  JPanel panel = new JPanel();
  panel.add(new JLabel(message));
  getContentPane().add(panel);
  //버튼패널을 생성한다.
  JPanel bpanel = new JPanel();
  JButton button = new JButton("OK");
  bpanel.add(button);
  button.addActionListener(this);
  getContentPane().add(bpanel, BorderLayout.SOUTH);
  setDefaultCloseOperation(DISPOSE_ON_CLOSE);
  pack();
  setVisible(true); 
 }
 public void actionPerformed(ActionEvent e) {
  setVisible(false);
  dispose();
 }
}
 
3. Calculation.java

import java.util.*;
class Calculation { 
   //-------------------------------------------------------------------
   //스택을 이용하여 중위표현을 후위표현으로 바꾸는 메소드
   //-------------------------------------------------------------------
  String  postfix(String infixExp) {
   Double value;
   //숫자의 끝임을 알려주는 flag
   //소수점 수식도 처리하기 위해서...
      boolean endOfNumber = false;  
    String postfixExp = new String();
   ArrayStack stk = new ArrayStack();
  for(int i = 0; i < infixExp.length(); i++)
  {
    switch(infixExp.charAt(i))
    {
   //피연산자는 그대로 출력한다.
   case '0':
   case '1':
   case '2':
   case '3':
   case '4':
   case '5':
   case '6':
   case '7':
   case '8':
   case '9':
   case '.':
     postfixExp = postfixExp.concat(infixExp.charAt(i)+"");
     endOfNumber = true;
     break;
   //왼쪽괄호인 경우에는 스택에 push 한다.
   case '(':
     if(endOfNumber == true)   {
    postfixExp = postfixExp.concat(" ");
    endOfNumber = false;
     }
     stk.push(new Character('('));
     break;
   //우측괄호인 경우 좌괄호가 나올때까지 pop하여 출력하고
      //좌괄호는 pop하여 버린다.
   case ')':
     if(endOfNumber == true)   {
    postfixExp = postfixExp.concat(" ");
    endOfNumber = false;
     }    
     while(((Character)stk.peek()).charValue() != '(' )
    postfixExp = postfixExp.concat(((Character)stk.pop()).toString());
     Object openParen = stk.pop(); 
     break;
   case '+':
   case '-':
   case '*':
   case '/':
     if(endOfNumber == true)  {
    postfixExp = postfixExp.concat(" ");
    endOfNumber = false;
     }
     //연산자를 만나면 스택에서 그 연산자보다 낮은 우선순위의 연산자를 만날 때까지
              //팝하여 출력한 뒤에 자신을 푸시한다.(우선순위가 같거나 높은것은 팝한다.)
     while ( !stk.isEmpty() && ((Character)stk.peek()).charValue() != '('
               &&  getPrec(infixExp.charAt(i)) <= getPrec(((Character)stk.peek()).charValue()) )  { 
    postfixExp = postfixExp.concat(((Character)stk.pop()).toString());
     }
     stk.push(new Character(infixExp.charAt(i)));
     break;
    }
  }
  if(endOfNumber == true) {
    postfixExp = postfixExp.concat(" ");
    endOfNumber = false;
  }
  //모든 작업이 끝나면 스택에 있는 연산자들을 모두 팝하여 출력한다.
  while( !stk.isEmpty()) {
    postfixExp = postfixExp.concat(((Character)stk.pop()).toString());
  }
  return postfixExp;
  }
  //----------------------------------------------------------------------
  //후위표기 수식을 스택을 이용한 연산을 수행하는 메소드
  //----------------------------------------------------------------------
  Double result(String postfixExp) {
    Double value, buffer;
 String temp = new String();
 ArrayStack stk = new ArrayStack();
    for(int i=0; i<postfixExp.length(); i++)    {
        switch(postfixExp.charAt(i))    {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':  
            case '.':
    //여기까지는 아직 공백을 만나지 않았으므로 수식의 끝이 아니다.
                temp = temp.concat(postfixExp.charAt(i)+"");
                break;  
            case ' ':
    //공백을 만나서야 비로서 수식을 스택에 넣는다.
               //공백을 만나기전에 수식이 여러개 있었다면 temp에 붙어서 저장되어 있다.
                stk.push(new Double(temp));
                temp = new String();
                break;
            case '+':
                value = new Double(((Double)stk.pop()).doubleValue() + ((Double)stk.pop()).doubleValue());
                stk.push(value);
                break;
            case '-':
                buffer = new Double(((Double)stk.pop()).doubleValue());
                value = new Double(((Double)stk.pop()).doubleValue() - buffer.doubleValue());
                stk.push(value);
                break;
            case '*':
                value = new Double(((Double)stk.pop()).doubleValue() * ((Double)stk.pop()).doubleValue());
                stk.push(value);
                break;
            case '/':
                buffer = new Double(((Double)stk.pop()).doubleValue());
                value = new Double(((Double)stk.pop()).doubleValue() / buffer.doubleValue());
                stk.push(value);
                break;
        }
    }
 return (Double)stk.peek();
  }
  //------------------------------------------
  //연산자의 우선순위를 Return
  //------------------------------------------
  int getPrec(char op) {
    int prec = 0;
    switch (op)   {
      case '+':
      case '-':
        prec = 1;
        break;
      case '*':
      case '/':
        prec = 2;
        break;
    }
    return prec;
  }
    //-----------------------------------------
    //괄호의 정확성 검사
 //-----------------------------------------
    static boolean bracketsBalance (String exp) {
  ArrayStack stk = new ArrayStack(exp.length() +1);
  for (int i = 0; i < exp.length(); i++) {
    //Scan across the __EXPRESSION__
    char ch = exp.charAt(i);
    if (  ch== '[' || ch == '('  )  {
    stk.push( new Character(ch));
    }        
    else if(ch == ']' || ch == ')')  { 
    //empty means brackets unmatched
    if (stk.isEmpty())   return false;
    char charFromStack = ((Character)stk.pop()).charValue();
    if (  ch == ']' && charFromStack != '['
      ||  (ch == ')' && charFromStack != '(')  )
      return false;
   } // end if
  } // end for loop
  return stk.isEmpty();  //empty means matched,  else unmatched
 }
}

4. Calc.java

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
//------------------------------------------------------------------
//
//
//     Main Class
//     - main method
//
//------------------------------------------------------------------
class Calc extends JFrame {
    public static void main(String[] args) {
        JFrame myCalc = new CalcWindow(); // Call the constructor
  //System exit로 종료, 오직 Application에서만 사용가능
        myCalc.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        myCalc.setVisible(true);
    }
}
 
 

//--------------------------------------------------------------------------------
//  Class Name : CalcWindow
//  Desc :  계산기의 GUI를 구성합니다.
//  Method :
//   Constructot : 생성자에서 GUI 부분을 완성한다.
//   void action_display(ActionEvent e) : 버튼등 만들어진 객체의 실제 이벤트 처리를 합니다.
//
//   아래는 생성자 부분의 리스너를 만드는 부분입니다.
//           ActionListener calcListener = new ActionListener() {
//                 public void actionPerformed(ActionEvent e) {
//                   action_display(e);  // process the
//                 }
//             };
//
//             이부분에서 action_display 메소드를 call 합니다. 즉 버튼등 이벤트
//             처리할것을 만들면 리스너를    addActionListener 명령으로
//             리스너를 Add해야 이벤트 처리가 가능합니다.
//
//
//----------------------------------------------------------------------------------
class CalcWindow extends JFrame {
    JTextField displayField;              // 수식을 입력, 결과 display창
    double     resultValue    = 0.0;    //  스택에 의한 계산 결과값을 받는변수 
 String     postfix;                        // 후위로 바꾸어 이를 보고나하는 변수
    static Font biggerFont = new Font("monspaced", Font.PLAIN, 24);
    public CalcWindow() {
        //--- 상단의 TextField  
        displayField = new JTextField(" ", 12); 
        displayField.setHorizontalAlignment(JTextField.TRAILING);
        displayField.setFont(biggerFont);
        //--- Clear button
        JButton clearButton = new JButton("CLEAR");
        clearButton.setFont(biggerFont);
  //--- 리스너들, 여기서는 clear용및 상단의 displayField 에
  //--- 값을 추가시키는 두가지의 리스너 종류가 존재한다.
  //--- Clear Button 이벤트 처리용 리스너
        clearButton.addActionListener(new ActionListener() {
                  public void actionPerformed(ActionEvent e) {
                      action_clear();
                  }
               });
        //--- 버튼을 누르면 상단의 JTextField에 출력하기 위한 리스너
        ActionListener calcListener = new ActionListener() {
                 public void actionPerformed(ActionEvent e) {
                     action_display(e);  // process the
                 }
             };
        // 계산기 숫자키 및 괄호 키 Button Create
        String buttonOrder = "1234567890().";
        JPanel buttonPanel = new JPanel(new GridLayout(5, 3));
        for (int i = 0; i < buttonOrder.length(); i++) {
            JButton b = new JButton(buttonOrder.substring(i, i+1));
            b.addActionListener(calcListener);
            b.setFont(biggerFont);
            buttonPanel.add(b);
        }

        //--- Create panel, listener, and buttons for all the operations
        JPanel opPanel = new JPanel(new GridLayout(5, 1));
       
        String opOrder = "+-*/=";
        for (int i = 0; i < opOrder.length(); i++) {
            JButton b = new JButton(opOrder.substring(i, i+1));
            b.addActionListener(calcListener);
            b.setFont(biggerFont);
            opPanel.add(b);
        }
        //--- Layout the top-level content panel
        Container content = this.getContentPane();
        content.setLayout(new BorderLayout(4, 4));   //4,4는 hgap, vgap
        content.add(displayField, BorderLayout.NORTH );
        content.add(buttonPanel , BorderLayout.CENTER);
        content.add(opPanel     , BorderLayout.EAST  );
        content.add(clearButton , BorderLayout.SOUTH );
        this.setTitle("Calc");
        this.pack();
    }//end constructor

  
    // Called by the action listener for numeric keys
    void action_display(ActionEvent e) {
        String string = e.getActionCommand(); //눌러진 키 값
        /////////////////////////////////////////////////////등호가 넘어오는 경우엔  스택을 이용하자...
  if (string.equals("="))  {
   Calculation c = new Calculation();
   boolean isOK = c.bracketsBalance(displayField.getText());  //괄호검사
   if (isOK) {
    postfix = c.postfix(displayField.getText());                        //후위표기로 바꿈
    resultValue = c.result(postfix).doubleValue();                   //계산후 결과받음
    displayField.setText("" + resultValue);                              //화면에 뿌려줌
   }
   else {
    MessageBox m = new MessageBox(this, "오류!", "괄호가 잘못 되었습니다...");
    action_clear();
   }
        }
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  else {
            // 등호가 아니라면 텍스트필드에 추가
            displayField.setText(displayField.getText() + string);
        }
    }

    //clear 버튼 처리시 호출되는 메소드
    void action_clear() {
        displayField.setText(" ");
        resultValue = 0;
    }
}

댓글 없음:

댓글 쓰기