001package fi.jyu.mit.ohj2; 002import java.io.*; 003import java.util.*; 004 005/** 006 * Luokka avustusten tulostamiseksi. Avustustiedoston muoto: 007 * <pre> 008 * [SISÄLLYS] - aina etsitään sisällystä tällä aihe- (topic) nimellä. 009 * Eka aihe - kerrotaan ekasta aiheesta 010 * Toka aihe - kerrotaan tokasta aiheesta 011 * 012 * [Eka aihe] 013 * Ekassa aiheessa voidaan kuvata mitä vaan ekaan aiheeseen liittyvää 014 * ja miksei muutakin. 015 * 016 * [Toka aihe] - aiheen otsikkorivillä saa olla kommentti 017 * Tokassa aiheessa sitten tokan aiheen sisällöstä. 018 * Tietysti aiheita voidaan kirjoittaa niin monta kuin halutaan 019 * eikä niiden kaikkien tarvitse olla sisällysluettelossa. 020 * Jos on kamalan pitkä aihe, jonka tulostus halutaan keskeyttää, 021 * voidaan avustustiedoston rivi aloittaa 022 * # 023 * risuaitamerkillä, joka pysäyttää tulostuksen. 024 * muutenkin tulostetaan vain korkeintaan 23 riviä kerrallaan. 025 * ; puolipisteellä alkava rivi on kommenttia ja sitä ei tulosteta 026 * </pre> 027 * Käyttöesimerkkejä:<br> 028 * Selaillaan koko avustusta. Tulostetaan ensin sisällysluettelo 029 * <pre> 030 * try { 031 * Help h = new Help("kerho.hlp"); 032 * h.browse(); 033 * } 034 * catch (IOException ioe) { 035 * System.err.println(ioe); 036 * } 037 * </pre> 038 * Tulostetaan valitun aiheen kohdalta: 039 * <pre> 040 * h.printMatchingTopics("Li*"); // tulostaa kaikki Li-alkavat aiheet 041 * h.printTopic("Lisäys"); // tulostaa aiheen Lisäys 042 * h.helpTopic("Li*"); // tulostaa kaikki Li-alkavat aiheet 043 * </pre> 044 * @author Vesa Lappalainen, Markku Vire 045 * @version 1.0, 24.03.2003 046 */ 047public class Help { 048 private static final char COMMENT = ';'; 049 private static final char TOPIC_START = '['; 050 private static final char TOPIC_END = ']'; 051 private static final char HELP_PAUSE = '#'; 052 private static final char QUIT_CHAR = 'q'; 053 private static final int DEFAULT_LINES = 23; 054 private static final String INDEX = "SIS*LLYS"; 055 056 private final Map<String,Collection<String>> topics = new HashMap<String,Collection<String>>(); 057 private int lines = DEFAULT_LINES; 058 private int printedLineCounter = 0; 059 private final BufferedReader consoleIn = new BufferedReader(new InputStreamReader(System.in)); 060 061 private PrintStream out = System.out; 062 private boolean stdout = true; 063 064 /** 065 * @return kuinka monta riviä tulostetaan korkeintaan pysähtymättä 066 */ 067 public int getLines() { return lines; } 068 069 /** 070 * @param lines pysähtymättä tulostettavien rivien lukumäärä 071 */ 072 public void setLines(int lines) { 073 if ( lines > 0 ) 074 this.lines = lines; 075 } 076 077 /** 078 * @param input tutkittava rivi 079 * @return alkaako rivi lopetusmerkillä 080 */ 081 private static boolean quit(String input) { 082 return firstIs(input, QUIT_CHAR); 083 } 084 085 /** 086 * @param s tutkittava rivi 087 * @param c merkki jota etsitään 088 * @return onko c rivin s ensimmäinen merkki (true) vai ei (false) 089 */ 090 private static boolean firstIs(String s, char c) { 091 if ( s == null || s.length() == 0 ) 092 return false; 093 094 return (Character.toUpperCase(s.charAt(0)) == Character.toUpperCase(c)); 095 } 096 097 /** 098 * Pysähtyy odottamaan returnin panamista jos on tulostettuja rivejä 099 * edellisen pysähtymisen jälkeen. 100 * @return onko painettu postumista (true) vai ei (false) 101 */ 102 private boolean helpStop() { 103 if ( !stdout ) return false; 104 String input = null; 105 106 if (printedLineCounter != 0) { 107 printedLineCounter = 0; 108 System.out.print("Jatka ENTER > "); 109 try { 110 input = consoleIn.readLine(); 111 } catch (IOException ioe) { 112 System.out.println(ioe); 113 } 114 } 115 116 return quit(input); 117 } 118 119 /** 120 * Lisätään uusi aihe-otsikko avustukseen. 121 * Tämän jälkeen rivejä voi lisätä otsikon alle 122 * <pre> 123 * Collection topic = h.addTopic("Uusi aihe"); 124 * topic.add("Eka rivi"); 125 * topic.add("Toka rivi"); 126 * </pre> 127 * Mikäli aihe on jo olemassa, palautetaan viite vanhaan aiheeseen. 128 * @param topic lisättävän aiheen otsikko 129 * @return tietorakenne, johon voi lisätä rivejä aiheen alle 130 */ 131 public final Collection<String> addTopic(String topic) { 132 String uptopic = topic.toUpperCase(); // NOPMD 133 Collection<String> newtopic = topics.get(uptopic); 134 if ( newtopic != null ) return newtopic; 135 136 newtopic = new ArrayList<String>(); 137 topics.put(uptopic,newtopic); 138 return newtopic; 139 } 140 141 /** 142 * Lisätään yksi rivi avustukseen aiheen topic alle. Mikäli aihetta 143 * ei vielä ole, se luodaan. 144 * @param topic lisättävän aiheen otsikko 145 * @param line lisättävä rivi 146 */ 147 public void addLine(String topic, String line) { 148 Collection<String> topicLines = addTopic(topic); 149 topicLines.add(line); 150 } 151 152 /** 153 * Alustetaan tyhjä avustus, johon voi lisätä aiheita 154 * metodeilla: addTopic, addLine, readFile. 155 */ 156 public Help() { 157 // ei tarvii tehdä mitään toistaiseksi 158 } 159 160 161 /** 162 * Asetetaan tulostusvirta toiseen paikkaan 163 * @param newout mihin tulostus tehdään? 164 */ 165 public void setOut(PrintStream newout) { 166 out = newout; 167 stdout = false; 168 } 169 170 171 172 /** 173 * Lukee avustuksen tiedostosta. Voidaan kutsua useita kertoja 174 * jolloin voidaan yhdistää monia avustustiedostoja. 175 * 176 * Tiedot kerätään map-tauluun, jossa aiheiden mukaiset merkkijonot 177 * avaimina (isoksi muutettuna). Aiheen alaiset rivit on 178 * sitten vektorina "avaimen oliona". 179 * @param fileName tiedosto,josta avustukset luetaan 180 * @throws IOException jos jokin menee pieleen tiedoston lukemisessa 181 */ 182 public final void readFile(String fileName) throws IOException { 183 @SuppressWarnings("resource") 184 BufferedReader in = new BufferedReader(new FileReader(fileName)); 185 Collection<String> topic = null; 186 String line; 187 try { 188 189 while ( (line = in.readLine()) != null ) { 190 int comment = line.indexOf(COMMENT); 191 192 if ( comment >= 0 ) line = line.substring(0, comment); 193 194 if ( firstIs(line, TOPIC_START) ) { 195 int topicEnd = line.indexOf(TOPIC_END); 196 197 if (topicEnd >= 0) { 198 String topicName = line.substring(1, topicEnd); 199 200 topic = addTopic(topicName); 201 } 202 } else { 203 if (topic != null) 204 topic.add(line); 205 } 206 } 207 } finally { 208 in.close(); 209 } 210 } 211 212 /** 213 * Alustaa avustuksen lukemalla avustukset tiedostosta. 214 * Tiedot kerätään map-tauluun, jossa aiheiden mukaiset merkkijonot 215 * avaimina (isoksi muutettuna). Aiheen alaiset rivit on 216 * sitten vektorina "avaimen oliona". 217 * @param fileName tiedosto,josta avustukset luetaan 218 * @throws IOException jos jokin menee pieleen tiedoston lukemisessa 219 */ 220 public Help(String fileName) throws IOException { 221 readFile(fileName); 222 } 223 224 /** 225 * Tulostaa seuraavan rivin Help-tekstiä. Jos rivimäärä ylittyy, 226 * niin pysähtyy. 227 * @param text tulostettava rivi 228 * @return painettiinko poistumista (true) vai ei (false) tulostuksen aikana 229 */ 230 private boolean helpPrint(String text) { 231 if ( firstIs(text, HELP_PAUSE) ) return helpStop(); 232 233 out.println(text); 234 printedLineCounter++; 235 236 if ( printedLineCounter >= lines ) return helpStop(); 237 238 return false; 239 } 240 241 /** 242 * Tulostaa valitun lohkon avustuksesta. 243 * @param topic tulostettava lohko. Ei saa sisältää jokereita 244 * @return painettiinko poistumista (true) vai ei (false) tulostuksen aikana 245 */ 246 public boolean printTopic(String topic) { 247 Collection<String> topicText = topics.get(topic.toUpperCase()); // NOPMD 248 249 if ( topicText == null ) 250 return helpPrint("Aihetta " + topic + " ei löydy"); 251 252 for (Iterator<String> i = topicText.iterator(); i.hasNext(); ) 253 if ( helpPrint(i.next()) ) 254 return true; 255 256 return false; 257 } 258 259 /** 260 * Tulostaa ne avustuksen lohkot jotka täsmäävät hakuehtoon. 261 * @param topic mahdolisesti jokereita * ja ? sisältävä ehto 262 * @return painettiinko poistumista (true) vai ei (false) tulostuksen aikana 263 */ 264 public boolean printMatchingTopics(String topic) { 265 printedLineCounter = 0; 266 267 if ( WildChars.containsWildChars(topic) ) { 268 for ( Iterator<String> i = topics.keySet().iterator(); i.hasNext() ; ) { 269 String s = i.next(); 270 if ( WildChars.onkoSamat(s, topic) && printTopic(s) ) 271 return true; 272 } 273 return false; 274 } 275 return printTopic(topic); 276 } 277 278 /** 279 * Selailee avustusta valitun lohkon kohdalta. Jos lohko == null 280 * niin näytetään kohta [SISÄLLYS] 281 * @param topic tulostettava avustuksen kohta 282 */ 283 public void browse(String topic) { 284 String newtopic = topic; 285 while (true) { 286 try { 287 if (newtopic == null) newtopic = INDEX; 288 289 if ( printMatchingTopics(newtopic) || !stdout ) return; 290 291 System.out.print("Valitse aihe (voi olla myös *) > "); 292 newtopic = consoleIn.readLine(); 293 if ( newtopic.length() == 0 ) break; 294 if ( quit(newtopic) ) break; 295 } 296 catch (IOException ioe) { 297 System.out.println(ioe); 298 } 299 } 300 } 301 302 /** 303 * Selailee avustusta aloittaen kohdasta [SISÄLLYS] 304 */ 305 public void browse() { browse(null); } 306 307 /** 308 * Tulostaa topic:in mukaisen lohkon avustuksesta. 309 * @param topic tulostetavan lohkon otsikko tai null jolloin selailee avustusta 310 */ 311 public void helpTopic(String topic) { 312 if ( topic == null ) browse(); 313 else printMatchingTopics(topic); 314 } 315 316 317 /** 318 * Testataa Help-luokkaa 319 * @param args ei käytössä 320 */ 321 public static void main(String[] args) { 322 try { 323 Help h = new Help(); //"kerho.txt"); 324 h.helpPrint("Terve!"); 325 h.helpTopic("Lisäys"); 326 h.helpPrint("#"); 327 h.browse(); 328 h.addLine("Uusi otsikko","Eka rivi"); 329 h.addLine("Uusi otsikko","Toka rivi"); 330 h.printMatchingTopics("Uusi*"); 331 } 332 catch (Exception ioe) { 333 System.err.println(ioe); 334 } 335 } 336}