For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!
Interested in getting your voice heard by members of the Developer Marketing team at Oracle? Check out this post for AppDev or this post for AI focus group information.
you should give your post a more specific name to make it more attractive to read for community members.
Your business logic currently is desktop.open(file);
l this causes the file to opem in the systems default application. Therefore its not quite helpfull.
instead you need to transfer the the content of your data file into Java Objects.
Unfortunately you did not tell what format your data files have. If you data file really is a MS office document (as the file extentions you coded indicate) then you should use the POI-Framefork. Of cause this will only hepl you to access the data. you still have to code the processing logic.
please elaborate mor on the format of your data file.
bye
TPD
The data exists within a Microsoft Office text file.
the data appears in the file as follows:
Country name: Total Sales:
Germany $562,000.00
China $1,585,985.00
America $5,285,025.00
India $625,254.00
Brazil $356,458.00
Russia $212,845.00
Canada $327,241.00
Australia $127,512.00
France $562,251.00
England $954,265.00
this is my test data file, I considered changing it to excel as it may be easier.
b22c36cf-64c5-485f-bc4f-820427531df0 wrote: The data exists within a Microsoft Office text file. this is my test data file, I considered changing it to excel as it may be easier.
b22c36cf-64c5-485f-bc4f-820427531df0 wrote:
You should consider to change it to a plain text format like CSV or XML or JSON since it is both even easier and less platform dependend.
I think I will try a CSV file format, it does seems as though it would make things a little easier.
b22c36cf-64c5-485f-bc4f-820427531df0 wrote: I think I will try a CSV file format, it does seems as though it would make things a little easier.
OK, that's a point where we can start.
first thing you have to do is to read the file.
This is best done line by line using a BuffrerdReader.
You should go through this totorial before you continue:
https://docs.oracle.com/javase/tutorial/essential/io/
so would this then be a more appropriate method of opening the file?
private void openFile(File file){
try(BufferedReader reader = Files.newBufferedReader(file, file){
String line = null;
while ((line = reader.readLine()) != null){
System.out.println(line);
}
catch (IOException ex){
Logger.getLogger(BusinessSalesDataAnalysis.class.getName()).log(Level.SEVERE, null, ex);
b22c36cf-64c5-485f-bc4f-820427531df0 wrote: so would this then be a more appropriate method of opening the file? private void openFile(File file){ try(BufferedReader reader = Files.newBufferedReader(file, file){ String line = null; while ((line = reader.readLine()) != null){ System.out.println(line); } catch (IOException ex){ Logger.getLogger(BusinessSalesDataAnalysis.class.getName()).log(Level.SEVERE, null, ex); } }
Yes.
next step is to split the line appart to get the information you need.
I'd suggest to use Pattern and Matcher from Java API.
These seem a little confusing, where would this go in the code?
Am I correct in assuming these are public classes that will exist outside of the main code? And would be invoked within:
b22c36cf-64c5-485f-bc4f-820427531df0 wrote: Am I correct in assuming these are public classes that will exist outside of the main code?
Follow the links I provided for examples of usage.
And would be invoked within:
yes.
This is what I have so far, I am pretty sure that I am not doing this right though.
public class PatternMatcher extends BusinessSalesDataAnalysis implements Serializable{ private static final Pattern p = Pattern.compile("Company Name:('[^ ]'+) Total Sales ('[\d$");
public static void main (String[] args){
public static void find(String text){ System.out.println(text); Matcher m = p.matcher(text); if(!m.matches() ) return;
}}
also where within:
would I invoke the pattern and matcher?
b22c36cf-64c5-485f-bc4f-820427531df0 wrote: This is what I have so far, I am pretty sure that I am not doing this right though.
Thats OK, nobody is perfect right fron the start.
Before we continue its time for another OOP pattern: separation of concerns.
this means that you should create rather small classes with very limited responsibilities.
So far you hav a class that does 2 things: reading a file and contrlling the program flow. splitting that up in seperate classe is called "refactoring" bit for now we only want th code not getting worse. This means we choose the next resposnibility and create a class that takes it.
You came up with the parsing part so we will create a new class LineParser.
What are the obsticals that your line parser will face?
When we look at your sample data the fist line is a header and should be ignored, right?
So lets formulate that as a JUnitTest:
import org.junit.Test; public class LineParserTest { @Test public void testParse_onHeaderLine_doesNothing() throws Exception { } }
import org.junit.Test;
public class LineParserTest {
@Test
public void testParse_onHeaderLine_doesNothing() throws Exception {
At this point another decision is to be made: what is the expected output of the LineParser class?
What first comes in mind is an array of Strings. But what would us help that in the long run? We have to check the result of the line Parser vor validity and then convert the Total Sales to numbers somewhere else.
so the next best idea is to return a custom Object, maybe a bean (aka DTO). But again wee would need special tradmend for the header line in the file.
I rather follow one more OO-principle: Tell! Don't ask.
In this case this means I simply throw all lines in and when finished I ask the class what it has.
BTW: you wrote
calculate percentages based on total sales of each group to overall sales of company, and add an organizational value code to each group
Since both columns in your sample data contain distinct values I cannot see what "grouping" is behind that data. For you I assume that the countries can appear more than once. If there are other criterias you have to either add that information in this CSV file or give that mapping in another CSV file.
To calculate some statistics you need more that one value for a group, so wee need an association etween the group (its name as a String by any chance) and the list of values in this group. It seams that we work with currencey. Therefore whe choose BigDecimal as the number type any primitive type is dangerous here.
this leads to a Map that assosiates a String to a List of BigDecimals as our return type.
the complete Test would look like this:
import static org.junit.Assert.*; import java.math.BigDecimal; import java.util.List; import java.util.Map; import org.junit.Test; public class LineParserTest { @Test public void testParse_onHeaderLine_doesNothing() throws Exception { // initialise LineParser lineParser = new LineParser(); // act lineParser.parse("Country name: Total Sales:"); Map<String, List<BigDecimal>> parsedContent= lineParser.getContent(); // assert assertTrue(parsedContent.isEmpty()); } }
import static org.junit.Assert.*;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
// initialise
LineParser lineParser = new LineParser();
// act
lineParser.parse("Country name: Total Sales:");
Map<String, List<BigDecimal>> parsedContent= lineParser.getContent();
// assert
assertTrue(parsedContent.isEmpty());
don't be confsed by the name "Test". This is more like an executable specification of what you want the LineParser to do since it currently does not pass this test, nobody wrote the needed logic so far. You can prove this when you right click the test class in your IDE and select "run as JUnitTest".
Mos likely it will not run because you have compile errors. thats why you need to write the LinParser class now:
import java.math.BigDecimal; import java.util.List; import java.util.Map; public class LineParser { public void parse(String string) { } public Map<String, List<BigDecimal>> getContent() { return null; } }
public class LineParser {
public void parse(String string) {
public Map<String, List<BigDecimal>> getContent() {
return null;
Now you can run the test but it Fails with a NullPointerException. This is good becaus it shows, that the Test really Test our incomplete production code.
We can fix this easily:
public Map<String, List<BigDecimal>> getContent() { return new HashMap<>(); }
return new HashMap<>();
when you run the test again it is now green.
But our LineParser should be able of doing more, so we need another test:
import static org.junit.Assert.*; import java.math.BigDecimal; import java.util.List; import java.util.Map; import org.junit.Test; public class LineParserTest { @Test public void testParse_onHeaderLine_doesNothing() throws Exception { LineParser lineParser = new LineParser(); lineParser.parse("Country name: Total Sales:"); Map<String, List<BigDecimal>> parsedContent = lineParser.getContent(); assertTrue(parsedContent.isEmpty()); } @Test public void testParse_validLineWithUnknownGroup_AddsNewEntryWithSingleValue() throws Exception { LineParser lineParser = new LineParser(); lineParser.parse("Germany $562,000.00"); Map<String, List<BigDecimal>> parsedContent = lineParser.getContent(); assertEquals(1,parsedContent.size()); assertEquals(0,new BigDecimal(562000.00).compareTo(parsedContent.get("Germany").get(0))); } }
Map<String, List<BigDecimal>> parsedContent = lineParser.getContent();
public void testParse_validLineWithUnknownGroup_AddsNewEntryWithSingleValue() throws Exception {
lineParser.parse("Germany $562,000.00");
assertEquals(1,parsedContent.size());
assertEquals(0,new BigDecimal(562000.00).compareTo(parsedContent.get("Germany").get(0)));
you run the test again and you see our first test is green but the new test fails.
Now it is time to com back to your initial question: how to use the Pattern and Matcher class to parse our input line.
the first thing you should recognise that in the second test the passed string does not contain the header names. this means, the regular expression you created would never have a match.
what we want back from the input line is the country name and the sales value.
the country name is anything before an unspecified number of spaces and a $ sign.
After the $ sign whe have the value consisting of unspecified group of 3 digits as the integer part and two digits after the point as fraction.
"(.*\\S) +\\$((\\d{1,3},)*\\d{1,3}\\.\\d{2})"
What that means in particulat is explained in the API page of the Pattern class.
then the LineParser finally looks like this:
import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; public class LineParser { private static final Pattern linePattern = Pattern.compile("(.*\\S) +\\$((\\d{1,3},)*\\d{1,3}\\.\\d{2})"); private final HashMap<String, List<BigDecimal>> content = new HashMap<>(); public void parse(String string) { Matcher lineMatcher = linePattern.matcher(string); if (lineMatcher.find()) { String countryName = lineMatcher.group(1); if (!content.containsKey(countryName)) content.put(countryName, new ArrayList<>()); String ungroupedValue = lineMatcher.group(2).replaceAll(",", ""); content.get(countryName).add(new BigDecimal(ungroupedValue)); } } public Map<String, List<BigDecimal>> getContent() { return content; } }
import java.util.ArrayList;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
private static final Pattern linePattern = Pattern.compile("(.*\\S) +\\$((\\d{1,3},)*\\d{1,3}\\.\\d{2})");
private final HashMap<String, List<BigDecimal>> content = new HashMap<>();
Matcher lineMatcher = linePattern.matcher(string);
if (lineMatcher.find()) {
String countryName = lineMatcher.group(1);
if (!content.containsKey(countryName))
content.put(countryName, new ArrayList<>());
String ungroupedValue = lineMatcher.group(2).replaceAll(",", "");
content.get(countryName).add(new BigDecimal(ungroupedValue));
return content;
in your code you use this with the while loop:
String line; LineParser lineParser = new LineParser(); while ((line = reader.readLine()) != null) { lineParser.parse(line); } Map<String, List<BigDecimal>> content = lineParser.getContent();
String line;
while ((line = reader.readLine()) != null) {
lineParser.parse(line);
Map<String, List<BigDecimal>> content = lineParser.getContent();
as you can see there is no need here to handle the header line in any special way.
Now you can loop through the countries and do your statistic calculation most likely in classes of their own...
I forgot that we of cause need a third test to require the List in ore returned map:
@Test public void testParse_validLineWithKnownGroup_AddsSecondValueToKnownCountry() throws Exception { LineParser lineParser = new LineParser(); lineParser.parse("Germany $562,000.00"); lineParser.parse("Germany $562,000.99"); Map<String, List<BigDecimal>> parsedContent = lineParser.getContent(); assertEquals(1, parsedContent.size()); assertEquals(new BigDecimal("562000.00"), parsedContent.get("Germany").get(0)); assertEquals(new BigDecimal("562000.99"), parsedContent.get("Germany").get(1)); }
public void testParse_validLineWithKnownGroup_AddsSecondValueToKnownCountry() throws Exception {
lineParser.parse("Germany $562,000.99");
assertEquals(1, parsedContent.size());
assertEquals(new BigDecimal("562000.00"), parsedContent.get("Germany").get(0));
assertEquals(new BigDecimal("562000.99"), parsedContent.get("Germany").get(1));
When I created your code I got an error on this line:
no suitable method found for put(String, ArrayList<Object>
method AbstractMap.put(String, List<BigDecimal>) is not applicable
(argument mismatch; ArrayList<Object> cannot be converted to List<BigDecimal>)
method HashMap.put(String, List<BigDecimal>) is not applicable
The error is because i removed the generics Parameter from the ArrayList constructor call when transfering my solution to the post.
I currently have Java 1.6 to test and I didn't realize that this generics Parameter would be needed here in Java 1.7/1.8.
I don't know if it is just that I have been looking at this for so long or that I really don't have a clue about what you are talking about but I am really lost. I have been trying to figure out that error all night to no avail, and now I am looking at the calculations section and realized ive never done calculations with data obtained from a lineParser before, it has always been done from inputs or hardcoded data. I need to get this done today as it is already a day late.
At this Point I cannot help anymore .
You did not write enough about your requirement and your sample data do not Support the few Points you did write.
What you have to do now is to pass the map you got from the LineParser to specific Statistic calculators Objects (one for each Statistic you want/Need to calculate.
Resist the temptation you Output the Statistics results right were you calculated them, rather retun them back to your main class and pass them to another class that maks the outut in a common manner. This way you can later Change it to Output to a file or to a GUI component later easily.
Think what return type would be suitable for all of the Statistics you have to calculate, so that you can in turn use the same outputter object.
Requirements:
1.) Read a file (.csv)
a.) sample data:
Germany, 562000.00
America, 5285025.00
China, 1585985.00
India, 625254.00 Brazil, 356458.00 Russia, 212854.00 Canada, 327241.00 Australia, 127512.00 France, 562251.00 England, 954265.00
2.) Calculate total sales
3.) Calculate each countries contributed percentage of that total
a.) percentage = country sales / total sales; return as a decimal4.) Assign an organizational value (1-5) based on their percentage
a.) 1 = 0-2% if (percentage <= 2) {
then ov = 1;
b.) 2 = 3-4% if (percentage >3 and <= 4) {
then ov = 2;
c.) 3 = 5-6% if (percentage >4 and <= 6) {
then ov = 3;
d.) 4 = 7-8% if (percentage >6 and <= 8) {
then ov = 4;
e.) 5 = 9+% if (percentage >8) {
then ov = 5;
5.) Display this information in a table on a GUI
6.) Be able to save this table to a file
as for the generics Parameter for the ArrayList, what is the generics Parameter?
OK, this is your complete home work assignment.
How would you Change the LineParserOutput to better suit that requirements?
Based on that Change or the current solution: how woild you solve Task 2?
Anthony_Reaper wrote: as for the generics Parameter for the ArrayList, what is the generics Parameter?
Anthony_Reaper wrote:
https://docs.oracle.com/javase/tutorial/java/generics/
I would say change it to read only the second value in each line and return those values, this would then be used to make the calculations. The GUI can be hardcoded to display the names of the countries alphabetically, the CSV file would be written alphabetically to match. Then the data can be displayed in order as read and still match up.
Task 2 : calculate total sales could be done by adding all of these values together using basic calculations, though I am still trying to figure out how to pass the values from List<BigDecimal> to a calculation. The return would be the total amount, this can then be passed to task three to calculate the percentages.
Task 3: use each value from List<BigDecimal> and divide them by the return value of the previous step. This return would be a decimal, multiply this decimal by 100 and return a whole value rounded to the nearest percent. This value can then be passed to the next task to calculate OV.
Task 4: use each percent passed from previous step to calculate the OV using a series of If/then statements. This information would then be passed to the GUI along with the percentages to be displayed in the table next to each country.
The GUI headers for the table and layout would be as follows
Country Sales Percent of total OV
hardcoded passed by LineParser passed by task 3 Passed by task 4
Thank you for that tutorial I am working on figuring out what it is I need for the ArrayList<> parameter. I am leaning towards BigDecimal though I may be a little off.
would it be possible for me to simply place the buttons and their ActionEvents into their own class like this?
public class Button {
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.stage.Stage;
import javafx.stage.FileChooser;
import java.io.BufferedReader;
import businesssalesdataanalysis.Java.LineParser;
uploadButton.setOnAction(
new EventHandler<ActionEvent>(){
@Override
public void handle(final ActionEvent e){
configureFileChooser(fileChooser);
File file = fileChooser.showOpenDialog(primaryStage);
if (file != null){
try {
openFile(file);
} catch (IOException ex) {
});
analyzeButton.setOnAction(
try{
calculateFile(file);
} catch (IOException ex){
private void openFile(File file) throws IOException{
try(BufferedReader reader = Files.newBufferedReader(File, null)){
private void calculateFile(File file) throws IOException{
Does this even look right?
Anthony_Reaper wrote: How would you Change the LineParserOutput to better suit that requirements? I would say change it to read only the second value in each line and return those values, this would then be used to make the calculations. The GUI can be hardcoded to display the names of the countries alphabetically, the CSV file would be written alphabetically to match. Then the data can be displayed in order as read and still match up.
how would you express that in code?
one step after the other!
first make it run, then make it nice.
I haven't done any changes yet and when I compiled the code this is what I am getting: to me it looks like a runtime error. Im not quite sure how to fix this.
Executing C:\Users\Anthony\Documents\NetBeansProjects\BusinessSalesDataAnalysis\dist\run2069178611\BusinessSalesDataAnalysis.jar using platform C:\Program Files (x86)\Java\jdk1.7.0_25\jre/bin/java
java.lang.NullPointerException
at java.nio.file.Files.newBufferedReader(Files.java:2674)
at businesssalesdataanalysis.BusinessSalesDataAnalysis.openFile(BusinessSalesDataAnalysis.java:119)
at businesssalesdataanalysis.BusinessSalesDataAnalysis.access$100(BusinessSalesDataAnalysis.java:41)
at businesssalesdataanalysis.BusinessSalesDataAnalysis$1.handle(BusinessSalesDataAnalysis.java:61)
at businesssalesdataanalysis.BusinessSalesDataAnalysis$1.handle(BusinessSalesDataAnalysis.java:54)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:69)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:217)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:170)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:38)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:37)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:53)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:28)
at javafx.event.Event.fireEvent(Event.java:171)
at javafx.scene.Node.fireEvent(Node.java:6863)
at javafx.scene.control.Button.fire(Button.java:179)
at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:193)
at com.sun.javafx.scene.control.skin.SkinBase$4.handle(SkinBase.java:336)
at com.sun.javafx.scene.control.skin.SkinBase$4.handle(SkinBase.java:329)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:64)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:33)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3328)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3168)
at javafx.scene.Scene$MouseHandler.access$1900(Scene.java:3123)
at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1563)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2265)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:250)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:173)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:292)
at com.sun.glass.ui.View.handleMouseEvent(View.java:528)
at com.sun.glass.ui.View.notifyMouse(View.java:922)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.access$100(WinApplication.java:29)
at com.sun.glass.ui.win.WinApplication$3$1.run(WinApplication.java:73)
at java.lang.Thread.run(Thread.java:724)
Exception in thread "JavaFX Application Thread"
The above error occurred when I tried to upload a file, im pretty sure that is important, to me that means that there is something wrong in this particular section of code:
my complete code looks like this right now:
_____________________________________Main class
package businesssalesdataanalysis;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
/**
*
* @author Anthony
*/
public final class BusinessSalesDataAnalysis extends Application {
private Path File;
public void start(final Stage primaryStage) {
primaryStage.setTitle("Business Sales Data Analysis");
final FileChooser fileChooser = new FileChooser();
final Button uploadButton = new Button("Upload a file");
final Button analyzeButton = new Button("Analyze the data");
final GridPane inputGridPane = new GridPane();
GridPane.setConstraints(uploadButton, 100, 50);
inputGridPane.setHgap(6);
inputGridPane.setVgap(6);
inputGridPane.getChildren().addAll(uploadButton, analyzeButton);
final Pane rootGroup = new VBox(12);
rootGroup.getChildren().addAll(inputGridPane);
rootGroup.setPadding(new Insets(12, 12, 12, 12));
primaryStage.setScene(new Scene(rootGroup));
primaryStage.show();
* @param args the command line arguments
public static void main(String[] args) {
Application.launch(args);
private static void configureFileChooser(final FileChooser fileChooser){
fileChooser.setTitle("Choose a File");
fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
fileChooser.getExtensionFilters().addAll(
new FileChooser.ExtensionFilter("All Files", "*.*"),
new FileChooser.ExtensionFilter("CSV", ".csv")
);
____________________________________________________LineParcer class
content.put(countryName, new ArrayList<BigDecimal>());
public Map<String, List<BigDecimal>> getContent(){
IF i am right I think the error is in the follow line of code:
specifically the null value, though I am not sure what should go there instead. Any ideas?
well I fixed that runtime error just to get another one I am really starting to hate life at this point. I am having way too much trouble :-(
It's not your life that didn't attend the lectures and started to homework a week to late.
Please post your current code and the complete error meaasge you get.