1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved.
   5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   6  *
   7  * This code is free software; you can redistribute it and/or modify it
   8  * under the terms of the GNU General Public License version 2 only, as
   9  * published by the Free Software Foundation.  Oracle designates this
  10  * particular file as subject to the "Classpath" exception as provided
  11  * by Oracle in the LICENSE file that accompanied this code.
  12  *
  13  * This code is distributed in the hope that it will be useful, but WITHOUT
  14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16  * version 2 for more details (a copy is included in the LICENSE file that
  17  * accompanied this code).
  18  *
  19  * You should have received a copy of the GNU General Public License version
  20  * 2 along with this work; if not, write to the Free Software Foundation,
  21  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  22  *
  23  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  24  * or visit www.oracle.com if you need additional information or have any
  25  * questions.
  26  */
  27 package com.sun.interview;
  28 
  29 import java.util.Map;
  30 import java.util.Vector;
  31 
  32 /**
  33  * A {@link Question question} to which the response is an array of strings.
  34  */
  35 public abstract class StringListQuestion extends Question
  36 {
  37     /**
  38      * Create a question with a nominated tag.
  39      * @param interview The interview containing this question.
  40      * @param tag A unique tag to identify this specific question.
  41      */
  42     protected StringListQuestion(Interview interview, String tag) {
  43         super(interview, tag);
  44         clear();
  45         setDefaultValue(value);
  46     }
  47 
  48     /**
  49      * Get the default response for this question.
  50      * @return the default response for this question.
  51      *
  52      * @see #setDefaultValue
  53      * @see #clear
  54      */
  55     public String[] getDefaultValue() {
  56         return defaultValue;
  57     }
  58 
  59     /**
  60      * Set the default response for this question,
  61      * used by the clear method.
  62      * @param v the default response for this question.
  63      *
  64      * @see #getDefaultValue
  65      * @see #clear
  66      */
  67     public void setDefaultValue(String[] v) {
  68         defaultValue = v;
  69     }
  70 
  71     /**
  72      * Specify whether or not duplicates should be allowed in the list.
  73      * By default, duplicates are allowed.
  74      * @param b true if duplicates should be allowed, and false otherwise
  75      * @see #isDuplicatesAllowed
  76      */
  77     public void setDuplicatesAllowed(boolean b) {
  78         duplicatesAllowed = b;
  79     }
  80 
  81     /**
  82      * Check whether or not duplicates should be allowed in the list.
  83      * @return true if duplicates should be allowed, and false otherwise
  84      * @see #setDuplicatesAllowed
  85      */
  86     public boolean isDuplicatesAllowed() {
  87         return duplicatesAllowed;
  88     }
  89 
  90     /**
  91      * Get the current (default or latest) response to this question.
  92      * @return The current value.
  93      *
  94      * @see #setValue
  95      */
  96     public String[] getValue() {
  97         return value;
  98     }
  99 
 100     /**
 101      * Verify this question is on the current path, and if it is,
 102      * return the current value.
 103      * @return the current value of this question
 104      * @throws Interview.NotOnPathFault if this question is not on the
 105      * current path
 106      * @see #getValue
 107      */
 108     public String[] getValueOnPath()
 109         throws Interview.NotOnPathFault
 110     {
 111         interview.verifyPathContains(this);
 112         return getValue();
 113     }
 114 
 115     /**
 116      * Get the response to this question as a string.
 117      * @return a string representing the current response to this question, or null.
 118      * @see #setValue(String)
 119      */
 120     public String getStringValue() {
 121         if (value == null)
 122             return null;
 123 
 124         StringBuffer sb = new StringBuffer();
 125         for (int i = 0; i < value.length; i++) {
 126             if (sb.length() > 0)
 127                 sb.append('\n');
 128             if (value[i] != null)
 129                 sb.append(value[i]);
 130         }
 131 
 132         return sb.toString();
 133     }
 134 
 135     public boolean isValueValid() {
 136         return true;
 137     }
 138 
 139     public boolean isValueAlwaysValid() {
 140         return false;
 141     }
 142 
 143     public void setValue(String s) {
 144         setValue(s == null ? null : split(s));
 145     }
 146 
 147     /**
 148      * Set the current value.
 149      * @param newValue The value to be set.
 150      *
 151      * @see #getValue
 152      */
 153     public void setValue(String[] newValue) {
 154         if (newValue != null) {
 155             for (int i = 0; i < newValue.length; i++) {
 156                 if (newValue[i] == null || (newValue[i].indexOf("\n") != -1))
 157                     throw new IllegalArgumentException();
 158             }
 159         }
 160 
 161         if (!equal(newValue, value)) {
 162             if (newValue == null)
 163                 value = null;
 164             else {
 165                 value = new String[newValue.length];
 166                 System.arraycopy(newValue, 0, value, 0, newValue.length);
 167             }
 168             interview.updatePath(this);
 169             interview.setEdited(true);
 170         }
 171     }
 172 
 173     /**
 174      * Clear any response to this question, resetting the value
 175      * back to its initial state.
 176      */
 177     public void clear() {
 178         setValue(defaultValue);
 179     }
 180 
 181     /**
 182      * Load the value for this question from a dictionary, using
 183      * the tag as the key.
 184      * @param data The map from which to load the value for this question.
 185      */
 186     protected void load(Map<String, String> data) {
 187         String o = data.get(tag);
 188         setValue(o);
 189     }
 190 
 191     /**
 192      * Save the value for this question in a dictionary, using
 193      * the tag as the key.
 194      * @param data The map in which to save the value for this question.
 195      */
 196     protected void save(Map<String, String> data) {
 197         if (value != null)
 198             data.put(tag, getStringValue());
 199     }
 200 
 201     /**
 202      * Compare two string arrays for equality.
 203      * @param s1 the first array to be compared, or null
 204      * @param s2 the other array to be compared, or null
 205      * @return true if both parameters are null, or if both are non-null
 206      * and are element-wise equal.
 207      * @see #equal(String, String)
 208      */
 209     protected static boolean equal(String[] s1, String[] s2) {
 210         if (s1 == null || s2 == null)
 211             return (s1 == s2);
 212 
 213         if (s1.length != s2.length)
 214             return false;
 215 
 216         for (int i = 0; i < s1.length; i++) {
 217             if (!equal(s1[i], s2[i]))
 218                 return false;
 219         }
 220 
 221         return true;
 222     }
 223 
 224 
 225     /**
 226      * Compare two strings for equality.
 227      * @param s1 the first string to be compared, or null
 228      * @param s2 the other string to be compared, or null
 229      * @return true if both parameters are null, or if both are non-null
 230      * and equal.
 231      */
 232     protected static boolean equal(String s1, String s2) {
 233         return (s1 == null ? s2 == null : s1.equals(s2));
 234     }
 235 
 236     /**
 237      * Split a string into a set of newline-separated strings.
 238      * @param s The string to be split, or null
 239      * @return an array of strings containing the newline-separated substrings of
 240      * the argument.
 241      */
 242     protected static String[] split(String s) {
 243         if (s == null)
 244             return empty;
 245 
 246         final char sep = '\n';
 247 
 248         Vector<String> v = new Vector<>();
 249         int start = -1;
 250         for (int i = 0; i < s.length(); i++) {
 251             if (s.charAt(i) == sep) {
 252                 if (start != -1)
 253                     v.addElement(s.substring(start, i));
 254                 start = -1;
 255             } else
 256                 if (start == -1)
 257                     start = i;
 258         }
 259         if (start != -1)
 260             v.addElement(s.substring(start));
 261         if (v.size() == 0)
 262             return empty;
 263         String[] a = new String[v.size()];
 264         v.copyInto(a);
 265         return a;
 266     }
 267 
 268     private static final String[] empty = { };
 269 
 270     /**
 271      * The current response for this question.
 272      */
 273     protected String[] value;
 274 
 275     /**
 276      * The default response for this question.
 277      */
 278     private String[] defaultValue;
 279 
 280     private boolean duplicatesAllowed = true;
 281 }