using contiki process class instead of relying on check box texts + added process source to checkboxes

This commit is contained in:
fros4943 2008-10-03 13:08:58 +00:00
parent bcd8f68ba5
commit c928a5eec5

View file

@ -26,7 +26,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. * SUCH DAMAGE.
* *
* $Id: ContikiMoteTypeDialog.java,v 1.43 2008/10/03 10:25:56 fros4943 Exp $ * $Id: ContikiMoteTypeDialog.java,v 1.44 2008/10/03 13:08:58 fros4943 Exp $
*/ */
package se.sics.cooja.contikimote; package se.sics.cooja.contikimote;
@ -231,16 +231,17 @@ public class ContikiMoteTypeDialog extends JDialog {
boolean foundAndSelectedProcess = false; boolean foundAndSelectedProcess = false;
for (Component processCheckBox : myDialog.processPanel.getComponents()) { for (Component processCheckBox : myDialog.processPanel.getComponents()) {
boolean inCompileFile = false; boolean inCompileFile = false;
ContikiProcess process = (ContikiProcess) ((JCheckBox) processCheckBox).getClientProperty("process");
for (File compileFile: moteTypeToConfigure.getCompilationFiles()) { for (File compileFile: moteTypeToConfigure.getCompilationFiles()) {
if (compileFile.getName().equals(((JCheckBox) processCheckBox).getToolTipText())) { if (process != null && compileFile.getName().equals(process.getSourceFile().getName())) {
inCompileFile = true; inCompileFile = true;
break; break;
} }
} }
if (inCompileFile && if (inCompileFile &&
presetProcess.equals(((JCheckBox) processCheckBox).getText())) { presetProcess.equals(process.getProcessName())) {
((JCheckBox) processCheckBox).setSelected(true); ((JCheckBox) processCheckBox).setSelected(true);
foundAndSelectedProcess = true; foundAndSelectedProcess = true;
break; break;
@ -1013,7 +1014,7 @@ public class ContikiMoteTypeDialog extends JDialog {
JPanel progressPanel = new JPanel(new BorderLayout()); JPanel progressPanel = new JPanel(new BorderLayout());
final JDialog progressDialog = new JDialog(myDialog, (String) null); final JDialog progressDialog = new JDialog(myDialog, (String) null);
JProgressBar progressBar; JProgressBar progressBar;
JButton button;
final MessageList taskOutput; final MessageList taskOutput;
progressDialog.setLocationRelativeTo(myDialog); progressDialog.setLocationRelativeTo(myDialog);
@ -1025,7 +1026,7 @@ public class ContikiMoteTypeDialog extends JDialog {
taskOutput = new MessageList(); taskOutput = new MessageList();
final Thread compilationThreadCopy = compilationThread; final Thread compilationThreadCopy = compilationThread;
button = new JButton("Close/Abort"); final JButton button = new JButton("Abort compilation");
button.addActionListener(new ActionListener() { button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (compilationThreadCopy != null && compilationThreadCopy.isAlive()) { if (compilationThreadCopy != null && compilationThreadCopy.isAlive()) {
@ -1123,15 +1124,15 @@ public class ContikiMoteTypeDialog extends JDialog {
Vector<String> userProcesses = new Vector<String>(); Vector<String> userProcesses = new Vector<String>();
for (Component checkBox : processPanel.getComponents()) { for (Component checkBox : processPanel.getComponents()) {
if (((JCheckBox) checkBox).isSelected()) { if (((JCheckBox) checkBox).isSelected()) {
userProcesses.add(((JCheckBox) checkBox).getText()); ContikiProcess process =
(ContikiProcess) ((JCheckBox) checkBox).getClientProperty("process");
userProcesses.add(process.getProcessName());
} }
} }
// Generate main contiki source file // Generate Contiki main file
String filename = null;
try { try {
filename = generateSourceFile(textID.getText(), sensors, coreInterfaces, generateSourceFile(textID.getText(), sensors, coreInterfaces, userProcesses);
userProcesses);
} catch (Exception e) { } catch (Exception e) {
libraryCreatedOK = false; libraryCreatedOK = false;
progressBar.setBackground(Color.ORANGE); progressBar.setBackground(Color.ORANGE);
@ -1165,66 +1166,52 @@ public class ContikiMoteTypeDialog extends JDialog {
mapFile.delete(); mapFile.delete();
} }
compilationThread = new Thread(new Runnable() { // Add all project directories
public void run() { compilationFiles = (Vector<File>) myGUI.getProjectDirs().clone();
// Add all project directories
compilationFiles = (Vector<File>) myGUI
.getProjectDirs().clone();
if (moteTypeProjectDirs == null || moteTypeProjectDirs.isEmpty()) {
compilationFiles.add(new File(textCoreDir.getText(), "testapps"));
} else {
compilationFiles.addAll(moteTypeProjectDirs);
}
if (moteTypeProjectDirs == null || moteTypeProjectDirs.isEmpty()) { // Add source files from project configs
compilationFiles.add(new File(textCoreDir.getText(), "testapps")); String[] projectSourceFiles =
} else { newMoteTypeConfig.getStringArrayValue(ContikiMoteType.class, "C_SOURCES");
compilationFiles.addAll(moteTypeProjectDirs); for (String projectSourceFile : projectSourceFiles) {
} if (!projectSourceFile.equals("")) {
File file = new File(projectSourceFile);
// Add source files from project configs if (file.getParent() != null) {
String[] projectSourceFiles = newMoteTypeConfig.getStringArrayValue( // Find which project directory added this file
ContikiMoteType.class, "C_SOURCES"); File projectDir = newMoteTypeConfig.getUserProjectDefining(
for (String projectSourceFile : projectSourceFiles) { ContikiMoteType.class, "C_SOURCES", projectSourceFile);
if (!projectSourceFile.equals("")) { if (projectDir != null) {
File file = new File(projectSourceFile); // We found a project directory; add it to path
if (file.getParent() != null) { compilationFiles.add(new File(projectDir.getPath(), file.getParent()));
// Find which project directory added this file
File projectDir = newMoteTypeConfig.getUserProjectDefining(
ContikiMoteType.class, "C_SOURCES", projectSourceFile);
if (projectDir != null) {
// We found a project directory; add it to path
compilationFiles.add(new File(projectDir.getPath(), file
.getParent()));
}
}
compilationFiles.add(new File(file.getName()));
} }
} }
compilationFiles.add(new File(file.getName()));
// Add selected process source files
for (Component checkBox : processPanel.getComponents()) {
if (((JCheckBox) checkBox).isSelected()) {
String fileName = ((JCheckBox) checkBox).getToolTipText();
if (fileName != null) {
compilationFiles.add(new File(fileName));
}
}
}
compilationSucceded = ContikiMoteTypeDialog.compileLibrary(identifier,
contikiDir, compilationFiles, symbolsCheckBox.isSelected(),
(ContikiMoteType.CommunicationStack) commStackComboBox.getSelectedItem(),
taskOutput.getInputStream(MessageList.NORMAL),
taskOutput.getInputStream(MessageList.ERROR));
}
}, "compilation thread");
compilationThread.start();
while (compilationThread.isAlive()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// NOP
} }
} }
// Add selected process source files
for (Component checkBox : processPanel.getComponents()) {
if (((JCheckBox) checkBox).isSelected()) {
ContikiProcess process =
(ContikiProcess) ((JCheckBox) checkBox).getClientProperty("process");
if (process.getSourceFile() != null) {
compilationFiles.add(process.getSourceFile().getParentFile());
compilationFiles.add(process.getSourceFile());
}
}
}
compilationSucceded = ContikiMoteTypeDialog.compileLibrary(identifier,
contikiDir, compilationFiles, symbolsCheckBox.isSelected(),
(ContikiMoteType.CommunicationStack) commStackComboBox.getSelectedItem(),
taskOutput.getInputStream(MessageList.NORMAL),
taskOutput.getInputStream(MessageList.ERROR));
if (!compilationSucceded) { if (!compilationSucceded) {
if (libFile.exists()) { if (libFile.exists()) {
libFile.delete(); libFile.delete();
@ -1246,11 +1233,13 @@ public class ContikiMoteTypeDialog extends JDialog {
} }
if (libraryCreatedOK) { if (libraryCreatedOK) {
button.setText("Compilation succeeded!");
progressBar.setBackground(Color.GREEN); progressBar.setBackground(Color.GREEN);
progressBar.setString("compilation succeded"); progressBar.setString("compilation succeded");
button.grabFocus(); button.grabFocus();
myDialog.getRootPane().setDefaultButton(createButton); myDialog.getRootPane().setDefaultButton(createButton);
} else { } else {
button.setText("Compilation failed!");
progressBar.setBackground(Color.ORANGE); progressBar.setBackground(Color.ORANGE);
progressBar.setString("compilation failed"); progressBar.setString("compilation failed");
myDialog.getRootPane().setDefaultButton(testButton); myDialog.getRootPane().setDefaultButton(testButton);
@ -1669,18 +1658,18 @@ public class ContikiMoteTypeDialog extends JDialog {
* @return Process definitions found under rootDirectory, {sourcefile, * @return Process definitions found under rootDirectory, {sourcefile,
* processname} * processname}
*/ */
public static Vector<String[]> scanForProcesses(File rootDirectory) { public static Vector<ContikiProcess> scanForProcesses(File rootDirectory) {
if (!rootDirectory.isDirectory()) { if (!rootDirectory.isDirectory()) {
logger.fatal("Not a directory: " + rootDirectory); logger.fatal("Not a directory: " + rootDirectory);
return null; return new Vector<ContikiProcess>();
} }
if (!rootDirectory.exists()) { if (!rootDirectory.exists()) {
logger.fatal("Does not exist: " + rootDirectory); logger.fatal("Does not exist: " + rootDirectory);
return null; return new Vector<ContikiProcess>();
} }
Vector<String[]> processes = new Vector<String[]>(); Vector<ContikiProcess> processes = new Vector<ContikiProcess>();
// Scan in rootDirectory // Scan in rootDirectory
try { try {
@ -1688,8 +1677,7 @@ public class ContikiMoteTypeDialog extends JDialog {
String cmdString = GUI.getExternalToolsSetting("CMD_GREP_PROCESSES") String cmdString = GUI.getExternalToolsSetting("CMD_GREP_PROCESSES")
+ " '" + rootDirectory.getPath().replace(File.separatorChar, '/') + " '" + rootDirectory.getPath().replace(File.separatorChar, '/')
+ "'/*.[ch]"; + "'/*.[ch]";
Pattern pattern = Pattern.compile(GUI Pattern pattern = Pattern.compile(GUI.getExternalToolsSetting("REGEXP_PARSE_PROCESSES"));
.getExternalToolsSetting("REGEXP_PARSE_PROCESSES"));
String[] cmd = new String[3]; String[] cmd = new String[3];
cmd[0] = GUI.getExternalToolsSetting("PATH_SHELL"); cmd[0] = GUI.getExternalToolsSetting("PATH_SHELL");
@ -1697,20 +1685,23 @@ public class ContikiMoteTypeDialog extends JDialog {
cmd[2] = cmdString; cmd[2] = cmdString;
Process p = Runtime.getRuntime().exec(cmd); Process p = Runtime.getRuntime().exec(cmd);
BufferedReader input = new BufferedReader(new InputStreamReader(p BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
.getInputStream()));
while ((line = input.readLine()) != null) { while ((line = input.readLine()) != null) {
Matcher matcher = pattern.matcher(line); Matcher matcher = pattern.matcher(line);
if (matcher.find()) { if (matcher.find()) {
processes.add(new String[]{matcher.group(1), matcher.group(2)}); File sourceFile = new File(rootDirectory, matcher.group(1));
if (!sourceFile.exists()) {
logger.fatal("Error during scan: Found file does not exist: " + sourceFile);
}
ContikiProcess process = new ContikiProcess(sourceFile, matcher.group(2));
processes.add(process);
} }
} }
input.close(); input.close();
// BufferedReader err = new BufferedReader(new InputStreamReader(p // BufferedReader err = new BufferedReader(new InputStreamReader(p.getErrorStream()));
// .getErrorStream()));
// if (err.ready()) // if (err.ready())
// logger.warn("Error occured during scan:"); // logger.warn("Error occurred during scan:");
// while ((line = err.readLine()) != null) { // while ((line = err.readLine()) != null) {
// logger.warn(line); // logger.warn(line);
// } // }
@ -1893,39 +1884,16 @@ public class ContikiMoteTypeDialog extends JDialog {
return foundProcesses; return foundProcesses;
} }
private boolean autoSelectDependencyProcesses(String processName, private boolean autoSelectDependencyProcesses(ContikiProcess process, boolean confirmSelection) {
String sourceFilename, boolean confirmSelection) {
// Locate source file if (process.getSourceFile() == null || !process.getSourceFile().exists()) {
File sourceFile = new File(textCoreDir.getText(), sourceFilename);
boolean foundFile = sourceFile.exists();
if (!foundFile) {
for (File projectDir : myGUI.getProjectDirs()) {
sourceFile = new File(projectDir, sourceFilename);
if (foundFile = sourceFile.exists()) {
break;
}
}
}
if (!foundFile) {
for (File projectDir : moteTypeProjectDirs) {
sourceFile = new File(projectDir, sourceFilename);
if (foundFile = sourceFile.exists()) {
break;
}
}
}
if (!foundFile) {
// Die quietly // Die quietly
return false; return false;
} }
Vector<String> autostartProcesses = null; Vector<String> autostartProcesses = null;
try { try {
autostartProcesses = parseAutostartProcesses(sourceFile); autostartProcesses = parseAutostartProcesses(process.getSourceFile());
} catch (Exception e) { } catch (Exception e) {
return false; return false;
} }
@ -1936,27 +1904,26 @@ public class ContikiMoteTypeDialog extends JDialog {
} }
for (String autostartProcess : autostartProcesses) { for (String autostartProcess : autostartProcesses) {
// Does this process already exist? /* Is dependency process already selected? */
boolean processAlreadySelected = false; boolean processAlreadySelected = false;
for (Component checkBox : processPanel.getComponents()) { for (Component checkBox : processPanel.getComponents()) {
JCheckBox checkBox2 = (JCheckBox) checkBox; ContikiProcess existingProcess =
String existingProcess = checkBox2.getText(); ((ContikiProcess) ((JCheckBox) checkBox).getClientProperty("process"));
boolean selected = checkBox2.isSelected(); boolean selected = ((JCheckBox) checkBox).isSelected();
if (existingProcess.equals(autostartProcess) && selected) { if (existingProcess.getProcessName().equals(autostartProcess) && selected) {
processAlreadySelected = true; processAlreadySelected = true;
} }
} }
if (!processAlreadySelected) { if (!processAlreadySelected) {
boolean processShouldBeSelected = false; boolean processShouldBeSelected = false;
if (confirmSelection) { if (confirmSelection) {
// Let user choose whether to add process // Let user choose whether to add process
Object[] options = { "Add", "Cancel" }; Object[] options = { "Add", "Cancel" };
String question = "The process " + processName String question = "The process '" + process.getProcessName()
+ " depends on the following process: " + autostartProcess + "' depends on the following process: '" + autostartProcess
+ "\nDo you want to select this as well?"; + "'\nDo you want to select this as well?";
String title = "Select dependency process?"; String title = "Select dependency process?";
int answer = JOptionPane.showOptionDialog(myDialog, question, title, int answer = JOptionPane.showOptionDialog(myDialog, question, title,
JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null,
@ -1966,28 +1933,30 @@ public class ContikiMoteTypeDialog extends JDialog {
processShouldBeSelected = true; processShouldBeSelected = true;
} }
} else { } else {
// Add process /* Add process */
processShouldBeSelected = true; processShouldBeSelected = true;
} }
if (processShouldBeSelected) { if (processShouldBeSelected) {
// Get checkbox to select /* Find checkbox to select */
JCheckBox processToSelectCheckBox = null; JCheckBox processToSelectCheckBox = null;
for (Component checkBox : processPanel.getComponents()) { for (Component checkBox : processPanel.getComponents()) {
JCheckBox checkBox2 = (JCheckBox) checkBox; ContikiProcess existingProcesses =
if (checkBox2.getText().equals(autostartProcess)) { (ContikiProcess) ((JCheckBox) checkBox).getClientProperty("process");
processToSelectCheckBox = checkBox2; if (existingProcesses != null && autostartProcess.equals(existingProcesses.getProcessName())) {
processToSelectCheckBox = ((JCheckBox) checkBox);
break; break;
} }
} }
if (processToSelectCheckBox == null) { if (processToSelectCheckBox == null) {
// Create new check box // Create new check box
processToSelectCheckBox = new JCheckBox(autostartProcess, true); processToSelectCheckBox = new JCheckBox(autostartProcess + " (unknown source)", true);
processToSelectCheckBox.setToolTipText("[unknown source file - autodependency]");
processToSelectCheckBox.putClientProperty("process", new ContikiProcess(null, autostartProcess));
processToSelectCheckBox.setAlignmentX(Component.LEFT_ALIGNMENT); processToSelectCheckBox.setAlignmentX(Component.LEFT_ALIGNMENT);
processToSelectCheckBox.setActionCommand("process_clicked: " processToSelectCheckBox.setActionCommand("process_clicked");
+ autostartProcess);
processToSelectCheckBox.addActionListener(myEventHandler); processToSelectCheckBox.addActionListener(myEventHandler);
processPanel.add(processToSelectCheckBox); processPanel.add(processToSelectCheckBox);
@ -2254,7 +2223,9 @@ public class ContikiMoteTypeDialog extends JDialog {
Vector<String> processes = new Vector<String>(); Vector<String> processes = new Vector<String>();
for (Component checkBox : processPanel.getComponents()) { for (Component checkBox : processPanel.getComponents()) {
if (((JCheckBox) checkBox).isSelected()) { if (((JCheckBox) checkBox).isSelected()) {
processes.add(((JCheckBox) checkBox).getText()); ContikiProcess process = (ContikiProcess)
((JCheckBox) checkBox).getClientProperty("process");
processes.add(process.getProcessName());
} }
} }
myMoteType.setProcesses(processes); myMoteType.setProcesses(processes);
@ -2338,44 +2309,44 @@ public class ContikiMoteTypeDialog extends JDialog {
} else if (e.getActionCommand().equals("scanprocesses")) { } else if (e.getActionCommand().equals("scanprocesses")) {
// Clear process panel // Clear process panel
processPanel.removeAll(); processPanel.removeAll();
Vector<String[]> processes = new Vector<String[]>(); Vector<ContikiProcess> processes = new Vector<ContikiProcess>();
/* Scan GUI project directories */ /* Scan GUI project directories */
for (File projectDir : myGUI.getProjectDirs()) { for (File projectDir : myGUI.getProjectDirs()) {
processes processes.addAll(ContikiMoteTypeDialog.scanForProcesses(projectDir));
.addAll(ContikiMoteTypeDialog.scanForProcesses(projectDir));
} }
/* If mote type specific project directories, scan the testapps directory */ /* If mote type specific project directories, scan the testapps directory */
if (moteTypeProjectDirs == null || moteTypeProjectDirs.isEmpty()) { if (moteTypeProjectDirs == null || moteTypeProjectDirs.isEmpty()) {
logger.info("No project directories selected, scanning testapps"); logger.info("No project directories selected, scanning testapps");
processes.addAll(ContikiMoteTypeDialog.scanForProcesses(new File( processes.addAll(ContikiMoteTypeDialog.scanForProcesses(
textCoreDir.getText(), "testapps"))); new File(textCoreDir.getText(), "testapps")));
} else { } else {
if (moteTypeProjectDirs != null) { if (moteTypeProjectDirs != null) {
for (File projectDir : moteTypeProjectDirs) { for (File projectDir : moteTypeProjectDirs) {
logger.info("Scanning " + projectDir.getPath()); logger.info("Scanning " + projectDir.getPath());
processes.addAll(ContikiMoteTypeDialog processes.addAll(ContikiMoteTypeDialog.scanForProcesses(projectDir));
.scanForProcesses(projectDir));
} }
} }
} }
if (processes != null) { if (processes.isEmpty()) {
for (String[] processInfo : processes) { logger.warn("No processes found during scan. Check project directories");
JCheckBox processCheckBox = new JCheckBox(processInfo[1], false); testButton.setEnabled(settingsOK = false);
processCheckBox.setToolTipText(processInfo[0]); } else {
for (ContikiProcess processInfo : processes) {
JCheckBox processCheckBox =
new JCheckBox(processInfo.getProcessName() + " (" + processInfo.getSourceFile().getName() + ")", false);
processCheckBox.setToolTipText(processInfo.getSourceFile().getAbsolutePath());
processCheckBox.putClientProperty("process", processInfo);
processCheckBox.setAlignmentX(Component.LEFT_ALIGNMENT); processCheckBox.setAlignmentX(Component.LEFT_ALIGNMENT);
processCheckBox.setActionCommand("process_clicked: " processCheckBox.setActionCommand("process_clicked");
+ processInfo[1]);
processCheckBox.addActionListener(myEventHandler); processCheckBox.addActionListener(myEventHandler);
processPanel.add(processCheckBox); processPanel.add(processCheckBox);
} }
} else {
logger.warn("No processes found during scan");
testButton.setEnabled(settingsOK = false);
} }
processPanel.revalidate(); processPanel.revalidate();
@ -2538,15 +2509,18 @@ public class ContikiMoteTypeDialog extends JDialog {
moteInterfacePanel.repaint(); moteInterfacePanel.repaint();
createButton.setEnabled(libraryCreatedOK = false); createButton.setEnabled(libraryCreatedOK = false);
} else if (e.getActionCommand().equals("addprocess")) { } else if (e.getActionCommand().equals("addprocess")) {
String newProcessName = JOptionPane.showInputDialog(myDialog, String newProcessName = (String) JOptionPane.showInputDialog(GUI.getTopParentContainer(),
"Enter process name"); "Enter Contiki process name manually:",
if (newProcessName != null) { "Enter process name", JOptionPane.PLAIN_MESSAGE, null, null,
JCheckBox processCheckBox = new JCheckBox(newProcessName, false); "");
processCheckBox.setAlignmentX(Component.LEFT_ALIGNMENT);
processCheckBox.setSelected(true);
processCheckBox if (newProcessName != null && !newProcessName.equals("")) {
.setActionCommand("process_clicked: " + newProcessName); JCheckBox processCheckBox = new JCheckBox(newProcessName + " (manually added)", true);
processCheckBox.setToolTipText("[unknown source file - manually added]");
processCheckBox.putClientProperty("process", new ContikiProcess(null, newProcessName));
processCheckBox.setAlignmentX(Component.LEFT_ALIGNMENT);
processCheckBox.setActionCommand("process_clicked");
processCheckBox.addActionListener(myEventHandler); processCheckBox.addActionListener(myEventHandler);
processPanel.add(processCheckBox); processPanel.add(processCheckBox);
@ -2577,27 +2551,14 @@ public class ContikiMoteTypeDialog extends JDialog {
} }
} else if (e.getActionCommand().equals("recheck_interface_dependencies")) { } else if (e.getActionCommand().equals("recheck_interface_dependencies")) {
recheckInterfaceDependencies(); recheckInterfaceDependencies();
} else if (e.getActionCommand().startsWith("process_clicked")) { } else if (e.getActionCommand().equals("process_clicked")) {
createButton.setEnabled(libraryCreatedOK = false);
boolean processWasSelected = false; JCheckBox checkBox = (JCheckBox) e.getSource();
String sourceFilename = null; ContikiProcess process = (ContikiProcess) checkBox.getClientProperty("process");
String processName = e.getActionCommand().split(": ")[1].trim();
// Was the process selected or unselected? if (checkBox.isSelected()) {
for (Component checkBox : processPanel.getComponents()) { autoSelectDependencyProcesses(process, true);
JCheckBox processCheckbox = (JCheckBox) checkBox;
if (processCheckbox.isSelected()
&& processCheckbox.getText().equals(processName)) {
processWasSelected = true;
sourceFilename = ((JCheckBox) checkBox).getToolTipText();
break;
}
}
if (!processWasSelected || sourceFilename == null) {
createButton.setEnabled(libraryCreatedOK = false);
} else {
autoSelectDependencyProcesses(processName, sourceFilename, true);
} }
} else { } else {